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)