From eb42b3643f591b4c04aa4b83d3f9e00d9d790620 Mon Sep 17 00:00:00 2001
From: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Date: Thu, 12 Sep 2024 11:16:59 +0000
Subject: [PATCH] Bug 38010: Add permissions

This patch adds a permissions store and utility methods for implementing permissions checks in a Vue app
---
 acqui/booksellers.pl                          | 12 +----
 .../prog/js/vue/components/Vendors/Main.vue   | 51 ++++++++++++++++++-
 .../components/Vendors/VendorContracts.vue    | 32 ++++++++++--
 .../Vendors/VendorSubscriptions.vue           | 16 ++++--
 .../prog/js/vue/modules/vendors.ts            |  3 ++
 .../prog/js/vue/routes/vendors.js             |  6 +--
 .../prog/js/vue/stores/permissions.js         | 21 ++++++++
 7 files changed, 119 insertions(+), 22 deletions(-)
 create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/stores/permissions.js

diff --git a/acqui/booksellers.pl b/acqui/booksellers.pl
index e2784430329..520b977f78f 100755
--- a/acqui/booksellers.pl
+++ b/acqui/booksellers.pl
@@ -52,7 +52,7 @@ The id of the supplier whose baskets we will display
 =cut
 
 use Modern::Perl;
-use C4::Auth qw( get_template_and_user );
+use C4::Auth qw( get_template_and_user haspermission );
 use C4::Budgets qw( GetBudgetHierarchy GetBudget CanUserUseBudget );
 use C4::Output qw( output_html_with_http_headers );
 use CGI qw ( -utf8 );
@@ -172,14 +172,4 @@ $template->param(
 );
 $template->{VARS}->{'allbaskets'} = $allbaskets;
 
-
-
-$template->param(
-    loop_suppliers => $loop_suppliers,
-    supplier       => ( $booksellerid || $supplier ),
-    count          => $supplier_count,
-    has_budgets          => $has_budgets,
-);
-$template->{VARS}->{'allbaskets'} = $allbaskets;
-
 output_html_with_http_headers $query, $cookie, $template->output;
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue
index b6cb622e039..ddb24f11414 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/Main.vue
@@ -14,7 +14,7 @@
                 </div>
 
                 <div class="col-md-2 order-sm-2 order-md-1">
-                    <LeftMenu :title="$__('Vendors')"></LeftMenu>
+                    <LeftMenu :title="$__('Vendor management')"></LeftMenu>
                 </div>
             </div>
         </div>
@@ -44,12 +44,16 @@ export default {
 
         const { loading, loaded, setError } = mainStore
 
+        const permissionsStore = inject("permissionsStore")
+        const { userPermissions } = storeToRefs(permissionsStore)
+
         return {
             vendorStore,
             AVStore,
             setError,
             loading,
             loaded,
+            userPermissions,
             AVStore,
         }
     },
@@ -90,6 +94,50 @@ export default {
             return Promise.all(promises)
         }
 
+        fetchConfig().then(() => {
+            this.loaded()
+            this.initialized = true
+        })
+    },
+    beforeCreate() {
+        this.loading()
+
+        const fetchConfig = () => {
+            let promises = []
+
+            const av_client = APIClient.authorised_values
+            const authorised_values = {
+                vendor_types: "VENDOR_TYPE",
+                vendor_interface_types: "VENDOR_INTERFACE_TYPE",
+                vendor_payment_methods: "VENDOR_PAYMENT_METHOD",
+                lang: "LANG",
+            }
+
+            let av_cat_array = Object.keys(authorised_values).map(function (
+                av_cat
+            ) {
+                return '"' + authorised_values[av_cat] + '"'
+            })
+
+            promises.push(
+                av_client.values
+                    .getCategoriesWithValues(av_cat_array)
+                    .then(av_categories => {
+                        Object.entries(authorised_values).forEach(
+                            ([av_var, av_cat]) => {
+                                const av_match = av_categories.find(
+                                    element => element.category_name == av_cat
+                                )
+                                this.AVStore[av_var] =
+                                    av_match.authorised_values
+                            }
+                        )
+                    })
+            )
+
+            return Promise.all(promises)
+        }
+
         fetchConfig().then(() => {
             this.vendorStore.currencies = currencies
             this.vendorStore.gstValues = gstValues.map(gv => {
@@ -103,6 +151,7 @@ export default {
         })
     },
     data() {
+        this.userPermissions = userPermissions
         return {
             initialized: true,
         }
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorContracts.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorContracts.vue
index 068d7a549c8..cb991ec9687 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorContracts.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorContracts.vue
@@ -8,8 +8,15 @@
                     <th scope="col">{{ $__("Description") }}</th>
                     <th scope="col">{{ $__("Start date") }}</th>
                     <th scope="col">{{ $__("End date") }}</th>
-                    <!-- PERMISSION CAN_user_acquisition_contracts_manage -->
-                    <th v-if="false" scope="col" class="NoSort noExport">
+                    <th
+                        v-if="
+                            isUserPermitted(
+                                'CAN_user_acquisition_contracts_manage'
+                            )
+                        "
+                        scope="col"
+                        class="NoSort noExport"
+                    >
                         {{ $__("Actions") }}
                     </th>
                 </tr>
@@ -29,8 +36,14 @@
                     <td :data-order="contract.contractenddate">
                         {{ contract.contractenddate }}
                     </td>
-                    <!-- PERMISSION CAN_user_acquisition_contracts_manage -->
-                    <td class="actions" v-if="false">
+                    <td
+                        class="actions"
+                        v-if="
+                            isUserPermitted(
+                                'CAN_user_acquisition_contracts_manage'
+                            )
+                        "
+                    >
                         <a
                             class="btn btn-default btn-xs"
                             :href="`/cgi-bin/koha/admin/aqcontract.pl?op=add_form&contractnumber=${contract.contractnumber}&booksellerid=${contract.booksellerid}`"
@@ -54,10 +67,21 @@
 </template>
 
 <script>
+import { inject } from "vue"
+
 export default {
     props: {
         vendor: Object,
     },
+    setup() {
+        const permissionsStore = inject("permissionsStore")
+
+        const { isUserPermitted } = permissionsStore
+
+        return {
+            isUserPermitted,
+        }
+    },
 }
 </script>
 
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorSubscriptions.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorSubscriptions.vue
index a0b7650ea33..419f5adeda3 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorSubscriptions.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Vendors/VendorSubscriptions.vue
@@ -3,24 +3,34 @@
         <h2>
             {{ $__("Subscription details") }}
         </h2>
-        <!-- PERMISSION -->
         <p>
             <strong>{{ $__("Number of subscriptions") }}: </strong>
             <a
-                v-if="false"
+                v-if="isUserPermitted('CAN_user_serials')"
                 :href="`/cgi-bin/koha/serials/serials-search.pl?bookseller_filter=${vendor.name}&searched=1`"
                 >{{ vendor.subscriptions.length }}</a
             >
-            <span v-if="true">{{ vendor.subscriptions.length }}</span>
+            <span v-else>{{ vendor.subscriptions.length }}</span>
         </p>
     </fieldset>
 </template>
 
 <script>
+import { inject } from "vue"
+
 export default {
     props: {
         vendor: Object,
     },
+    setup() {
+        const permissionsStore = inject("permissionsStore")
+
+        const { isUserPermitted } = permissionsStore
+
+        return {
+            isUserPermitted,
+        }
+    },
 }
 </script>
 
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/vendors.ts b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/vendors.ts
index 26e61adf75f..9424ae0b00c 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/vendors.ts
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/vendors.ts
@@ -23,6 +23,7 @@ import { useMainStore } from "../stores/main";
 import { useVendorStore } from "../stores/vendors";
 import { useAVStore } from "../stores/authorised-values";
 import { useNavigationStore } from "../stores/navigation";
+import { usePermissionsStore } from "../stores/permissions";
 import i18n from "../i18n";
 
 const pinia = createPinia();
@@ -30,6 +31,7 @@ const pinia = createPinia();
 const mainStore = useMainStore(pinia);
 const AVStore = useAVStore(pinia);
 const navigationStore = useNavigationStore(pinia);
+const permissionsStore = usePermissionsStore(pinia);
 const routes = navigationStore.setRoutes(routesDef);
 
 const router = createRouter({
@@ -52,6 +54,7 @@ app.provide("vendorStore", useVendorStore(pinia));
 app.provide("mainStore", mainStore);
 app.provide("AVStore", AVStore);
 app.provide("navigationStore", navigationStore);
+app.provide("permissionsStore", permissionsStore);
 
 app.mount("#__vendors");
 
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/routes/vendors.js b/koha-tmpl/intranet-tmpl/prog/js/vue/routes/vendors.js
index 8a2908e57cb..bfaffca3fdc 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/routes/vendors.js
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/routes/vendors.js
@@ -11,7 +11,7 @@ export const routes = [
         path: "/cgi-bin/koha/acqui/booksellers.pl",
         is_default: true,
         is_base: true,
-        title: $__("Vendors"),
+        title: $__("Vendor management"),
         children: [
             {
                 path: "",
@@ -21,8 +21,8 @@ export const routes = [
             },
             {
                 path: "/cgi-bin/koha/vendors",
-                title: $__("Vendor list"),
-                icon: "fa fa-check-circle",
+                title: $__("Vendors"),
+                icon: "fa fa-shopping-cart",
                 is_end_node: true,
                 children: [
                     {
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/permissions.js b/koha-tmpl/intranet-tmpl/prog/js/vue/stores/permissions.js
new file mode 100644
index 00000000000..1dfc1af7cf2
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/stores/permissions.js
@@ -0,0 +1,21 @@
+import { defineStore } from "pinia";
+
+export const usePermissionsStore = defineStore("permissions", {
+    state: () => ({
+        userPermissions: null,
+    }),
+    actions: {
+        isUserPermitted(operation, permissions) {
+            const userPermissions = permissions
+                ? permissions
+                : this.userPermissions;
+            if (!operation) return true;
+            if (!userPermissions) return false;
+
+            return (
+                userPermissions.hasOwnProperty(operation) &&
+                userPermissions[operation]
+            );
+        },
+    },
+});
-- 
2.39.3 (Apple Git-146)