iT邦幫忙

0

【kintone】在流程管理中執行操作時的事件「app.record.detail.process.proceed 」的編輯權限問題

  • 分享至 

  • xImage
  •  

在 kintone 客製化中,會透 kintone JavaScript API 註冊各種事件處理程式(event handler),以在特定的情況下觸發客製化程式碼運行。kintone JavaScript API 提供了各種事件可供使用,本文這次要探討的主角是:在流程管理中執行操作時的事件 app.record.detail.process.proceed

執行流程動作時,需要記錄編輯權限嗎?

在我們開始討論 app.record.detail.process.proceed 的問題前,先來解答這個問題:

執行流程動作時,執行者需要該筆記錄的編輯權限嗎?

如果你對 kintone 相當熟悉,可能已經知道答案了——答案是:不需要

根據官方說明 kintone 說明-流程管理的基本使用方法,指定為執行者的使用者,必須具備記錄的查看權限。若沒有記錄的查看權限,該使用者將無法查看記錄,也無法執行動作。沒有記錄的編輯權限則不影響。

執行者只需要有查看權限,沒有編輯權限也能執行流程動作。最常見的情境是申請表單的審核,如果只要讓審核者決定是否批准申請,但不要讓他可以修改申請內容,就可以透過記錄權限設定條件,讓審核者只能閱覽而不能編輯紀錄。

然而,有些用戶可能會遇到下面的情形:

明明有記錄閱覽權限,執行流程動作時卻跳出錯誤顯示「無權限」,不是說不用編輯權限也不影響嗎?

會遇到這樣的情形,通常是因為該應用程式有使用客製化程式碼或外掛程式,會在執行流程動作時進行一些處理。最簡單的排除方式是調整權限設定,讓執行者具備編輯權限,如果開啟了編輯權限就能夠順利執行動作,那麼問題八九不離十就是本文要討論的主角 —— app.record.detail.process.proceed 事件處理的問題。

app.record.detail.process.proceed 的特性

官方文件:在流程管理中執行操作時的事件

事件發生時機

app.record.detail.process.proceed (行動版:mobile.app.record.detail.process.proceed),以下簡稱為「process.proceed」,是發生在「在流程管理中執行操作之前」,也就是使用者點擊操作之後,在狀態正式更新以前的事件。

和「在新增記錄畫面上存儲時的事件(app.record.create.submit)」與「在記錄編輯畫面上存儲時的事件(app.record.edit.submit)」類似,是在執行動作後,到動作執行完成之間發生的事件。

event handler 可以不 return 任何東西

在 event handler 中,不 return 任何東西時,流程動作會正常執行,記錄的狀態會被更新。

取消執行操作

在 event handler 中,return false 或無效值,或是透過設定 event.error 值,在畫面頂部顯示錯誤訊息時,可以中斷執行操作。此時,因為操作被取消了,記錄的流程狀態不會被更新。

可透過事件物件執行的操作

可以透過改寫 event.record.欄位代碼.value 值,並且 return event 來更新欄位的值。
改寫欄位值時,需要有記錄與欄位的編輯權限。
可以改寫值的欄位類型請參考官方文件:可以用事件物件執行的操作-重寫欄位的值

改寫欄位值需要編輯權限,這是很合理的。但是你可能會遇到這樣的問題:

「我明明沒有修改欄位值啊,怎麼還是跳出無權限?」

只要 return event ,就需要編輯權限

雖然官方文件是在改寫欄位值的部分加註說需要記錄與欄位編輯權限,但實際上,不論你是否有改寫 event.record.欄位代碼.value 的值,只要在 event handler 內 return event ,就會被視為編輯動作。

所以,如果在事件處理中不需要編輯欄位值時,只要不 return 任何東西,流程就會繼續進行。

以下圖應用程式為例:

流程設計:

我們寫一支簡單的客製化程式碼,會在申請人執行「送審」時,將當下時間填入欄位「申請時間」,其他的動作則不做任何處理。

【客製化程式碼1】

kintone.events.on('app.record.detail.process.proceed', event => {
  const action = event.action.value

  // 執行此動作的使用者,需要有記錄編輯權限
  if (action === '送審') { 
    const today = new Date().toISOString()
    event.record['申請時間'].value = today
    return event // 在條件內 return event
  }

  // 其他動作不 return event,不需記錄編輯權限 (亦可直接省略 return)
  return 
})

由於在大多數的 kintone 事件當中,都需要 return event 才能正確動作,所以開發者們可能會很習慣地加上 return event ,但在上述的情形中就會遇上權限問題,所以使用 process.proceed 事件進行處理時一定要特別留意這點。

return event 的潛在問題

然而,不 return event 有一個潛在的問題,當一個應用程式有多支客製化程式碼或外掛程式運用到 process.proceed 事件處理時,更精確來說,當一個應用程式內觸發複數個 process.proceed event handler 時,可能會有事件處理之間的競爭問題。

我們繼續以方才的範例應用程式來說明,假設後來新增了一個需求,要在執行「送審」動作時檢查申請內容字數,字數太少就跳出提示並且取消動作。

【客製化程式碼2】

kintone.events.on('app.record.detail.process.proceed', event => {
  if (event.action.value !== '送審') return
  
  const content = event.record['申請內容']?.value || ''
  if (content.trim().length < 5) {
    window.alert('申請內容不得少於 5 字!')
    return false
  }
})

由於這個動作不需要修改欄位值,所以按照剛剛講的原則,不 return event,看起來似乎沒有問題對吧?

⋯⋯但是,請停下來思考一下,下面的這兩種情形,分別會產生什麼樣的結果:

  1. 【客製化程式碼1】 先執行,接著執行 【客製化程式碼2】
  2. 【客製化程式碼2】 先執行,接著執行 【客製化程式碼1】

如果是第2種情況,兩個程式碼都可以如預期運作;但如果是第1種情況,就會發生問題——在程式碼1當中應該要被更新的申請時間,在流程結束後還是空白的。

這是因為操作 event.record 修改欄位值時,必須要 return event 才會生效,但是程式碼1先執行的情況下,雖然有 return event ,但是進入到程式碼2後,最終並沒有再把 event return 出去,導致程式碼1中的編輯無效。

解決方案

本文的範例非常單純,在沒有其他第三方客製化內容或外掛程式的情況下,我們已知執行「送審」動作時會需要編輯權限,且會透過客製化程式碼編輯欄位值,所以只要在 【客製化程式碼2】 最後面補上 return event,或是確保 【客製化程式碼2】 先執行即可。

然而(對,又是這個然而),kintone 使用者很可能會因為需求的改變,需要追加客製化程式或是其他外掛,我們要如何避免自己寫的客製化程式碼和其他的程式發生衝突呢?

檢查編輯權限,再決定是否 return event

以目前 process.proceed 的性質來說,建議所有 kintone 開發者採用這個設計方式。即使開發的內容不需要編輯權限,但你無法確定用戶是否會使用其他的客製化程式或外掛,需要 return event 來完成動作。

可以運用「獲取執行API的用戶記錄的存取權限」的 REST API,先確認動作執行者是否具備該筆記錄之編輯權限,如果有編輯權限就 return event ,若沒有編輯權限則不 return 任何東西。所有客製化開發(含外掛)都遵循此原則時,就可以避免「該 return event 的狀況卻因為別的程式沒有 return 而發生衝突」的問題。

以下是 【客製化程式碼2】 的改良方案(完整範例):

(() => {
  'use strict'

  kintone.events.on('app.record.detail.process.proceed', async event => {
    const action = event.action.value

    // 檢查執行者是否有編輯權限
    let isEditable = false
    try {
      // 使用「獲取執行API的用戶記錄的存取權限」的 REST API
      const res = await getUserPermission()
      const right = res.rights[0]
      isEditable = right.record?.editable || false
    } catch (error) {
      console.error(error)
    }
    
    // 送審時,檢查申請內容字數
    if (action === '送審') {
      const content = event.record['申請內容']?.value || ''
      if (content.trim().length < 5) {
        window.alert('申請內容不得少於 5 字!')
        return false
      }
    }

    if (isEditable) {
      return event // 有編輯權限時,return event
    } else {
      return // 無編輯權限時,不 return 任何東西
    }
  })

  // 執行 REST API 的 function
  async function getUserPermission() {
    try {
      const baseUrl = window.location.origin + '/k/v1/records/acl/evaluate.json?'
      const paramString = new URLSearchParams({
        app: kintone.app.getId(),
        ids: [kintone.app.record.getId()]
      })
      const res = await fetch(baseUrl + paramString, {
        method: 'GET',
        headers: {
          'X-Requested-With': 'XMLHttpRequest'
        }
      })
      const data = await res.json()
      if (!res.ok) {
        console.error('API error', data)
        throw new Error('API error')
      }
      return data
    } catch (error) {
      throw error
    }
  }
})()

新的處理方式:保留編輯權限,但隱藏編輯按鈕

🌟 本段內容為 2025/8/13 更新。

在 kintone 2025 年 8 月版更新 中,新增了多項 JavaScript API,其中包含「切換是否顯示記錄編輯按鈕」的功能。

在自行開發(不考慮與第三方套件或外掛衝突)的情況下,如果需要在流程中透過 event.record 更新欄位值,但又不希望使用者在特定狀態下直接進行編輯,可以保留使用者的編輯權限,並透過此 API 動態隱藏編輯按鈕,達到限制編輯的效果。

// PC 版
kintone.app.record.showEditRecordButton(state)

// 行動版
kintone.mobile.app.record.showEditRecordButton(state)
  • 參數 state 接受字串 'VISIBLE'(顯示編輯按鈕)或 'HIDDEN'(隱藏編輯按鈕)
  • 此 API 為非同步 API,需搭配 await 或 Promise 處理。

範例程式碼

kintone.events.on('app.record.detail.show', async event => {
  await kintone.app.record.showEditRecordButton('HIDDEN')
  return event
})

結語

平心而論,process.proceed 是一個使用起來有點麻煩的事件,因為如果沒有考慮到 return event 的權限問題時,就很容易雷到別人(或是被別人雷到)⋯⋯針對這個議題,我們已經提出改善的建議,日後或許有機會優化。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言