chore: sandbox plan should not execution special workflow (#38662)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
非法操作 2026-07-13 10:55:39 +08:00 committed by GitHub
parent f4dfac7214
commit 7311f1ba6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 374 additions and 4 deletions

View File

@ -31,6 +31,12 @@ class NotWorkflowAppError(BaseHTTPException):
code = 400
class WorkflowVersionExecutionNotAllowedError(BaseHTTPException):
error_code = "workflow_version_execution_not_allowed"
description = "Workflow version execution is not available on your current plan. Please upgrade to a paid plan."
code = 403
class ConversationCompletedError(BaseHTTPException):
error_code = "conversation_completed"
description = "The conversation has ended. Please start a new conversation."

View File

@ -11,6 +11,7 @@ from pydantic.json_schema import SkipJsonSchema
from sqlalchemy.orm import Session, sessionmaker
from werkzeug.exceptions import BadRequest, InternalServerError, NotFound
from configs import dify_config
from controllers.common.controller_schemas import WorkflowRunPayload as WorkflowRunPayloadBase
from controllers.common.fields import GeneratedAppResponse, SimpleResultResponse
from controllers.common.schema import (
@ -27,6 +28,7 @@ from controllers.service_api.app.error import (
ProviderModelCurrentlyNotSupportError,
ProviderNotInitializeError,
ProviderQuotaExceededError,
WorkflowVersionExecutionNotAllowedError,
)
from controllers.service_api.schema import (
expect_user_json,
@ -43,6 +45,7 @@ from core.errors.error import (
QuotaExceededError,
)
from core.helper.trace_id_helper import get_external_trace_id, get_trace_session_id, omit_trace_session_id_from_payload
from enums.cloud_plan import CloudPlan
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from fields.base import ResponseModel
@ -56,6 +59,7 @@ from libs.helper import dump_response, to_timestamp
from models.model import App, AppMode, EndUser
from repositories.factory import DifyAPIRepositoryFactory
from services.app_generate_service import AppGenerateService
from services.billing_service import BillingService
from services.errors.app import IsDraftWorkflowError, WorkflowIdFormatError, WorkflowNotFoundError
from services.errors.llm import InvokeRateLimitError
from services.workflow_app_service import WorkflowAppService
@ -396,6 +400,10 @@ class WorkflowRunByIdApi(Resource):
"- `completion_request_error` : Workflow execution request failed.\n"
"- `invalid_param` : Required parameter missing or invalid."
),
403: (
"`workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the "
"current plan. Upgrade to a paid plan."
),
404: "`not_found` : Workflow not found.",
429: (
"- `too_many_requests` : Too many concurrent requests for this app.\n"
@ -421,6 +429,7 @@ class WorkflowRunByIdApi(Resource):
200: "Workflow executed successfully",
400: "Bad request - invalid parameters or workflow issues",
401: "Unauthorized - invalid API token",
403: "Forbidden - upgrade to a paid plan to execute a specific workflow version",
404: "Workflow not found",
429: "Rate limit exceeded",
500: "Internal server error",
@ -442,6 +451,11 @@ class WorkflowRunByIdApi(Resource):
if app_mode != AppMode.WORKFLOW:
raise NotWorkflowAppError()
if dify_config.BILLING_ENABLED:
billing_info = BillingService.get_info(app_model.tenant_id, exclude_vector_space=True)
if billing_info["enabled"] and billing_info["subscription"]["plan"] == CloudPlan.SANDBOX:
raise WorkflowVersionExecutionNotAllowedError()
payload = WorkflowRunPayload.model_validate(omit_trace_session_id_from_payload(service_api_ns.payload) or {})
args = payload.model_dump(exclude_none=True)
trace_session_id = get_trace_session_id(request)

View File

@ -2235,7 +2235,7 @@ Execute a specific workflow version identified by its ID. Useful for running a p
| 200 | Successful response. The content type and structure depend on the `response_mode` parameter in the request. - If `response_mode` is `blocking`, returns `application/json` with a `WorkflowBlockingResponse` object. - If `response_mode` is `streaming`, returns `text/event-stream` with a stream of `ChunkWorkflowEvent` objects. | **application/json**: [GeneratedAppResponse](#generatedappresponse)<br>**text/event-stream**: [GeneratedAppResponse](#generatedappresponse)<br> |
| 400 | - `not_workflow_app` : App mode does not match the API route. - `bad_request` : Workflow is a draft or has an invalid ID format. - `provider_not_initialize` : No valid model provider credentials found. - `provider_quota_exceeded` : Model provider quota exhausted. - `model_currently_not_support` : Current model unavailable. - `completion_request_error` : Workflow execution request failed. - `invalid_param` : Required parameter missing or invalid. | |
| 401 | Unauthorized - invalid API token | |
| 403 | Forbidden - token scope, app, dataset, or workspace access denied | |
| 403 | `workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the current plan. Upgrade to a paid plan. | |
| 404 | `not_found` : Workflow not found. | |
| 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The upstream model provider rate limit was exceeded. | |
| 500 | `internal_server_error` : Internal server error. | |

View File

@ -26,7 +26,7 @@ from flask import Flask
from sqlalchemy.orm import sessionmaker
from werkzeug.exceptions import BadRequest, NotFound
from controllers.service_api.app.error import NotWorkflowAppError
from controllers.service_api.app.error import NotWorkflowAppError, WorkflowVersionExecutionNotAllowedError
from controllers.service_api.app.workflow import (
AppQueueManager,
DifyAPIRepositoryFactory,
@ -42,11 +42,13 @@ from controllers.service_api.app.workflow import (
)
from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError
from core.app.entities.app_invoke_entities import InvokeFrom
from enums.cloud_plan import CloudPlan
from graphon.enums import WorkflowExecutionStatus
from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom
from models.model import App, AppMode, EndUser
from models.workflow import WorkflowAppLog, WorkflowAppLogCreatedFrom, WorkflowRun, WorkflowType
from services.app_generate_service import AppGenerateService
from services.billing_service import BillingService
from services.errors.app import IsDraftWorkflowError, WorkflowNotFoundError
from services.errors.llm import InvokeRateLimitError
from services.workflow_app_service import LogView, LogViewDetails, WorkflowAppService
@ -570,9 +572,117 @@ class TestWorkflowRunApi:
with pytest.raises(InvokeRateLimitHttpError):
handler(api, session=Mock(), app_model=app_model, end_user=end_user)
def test_sandbox_billing_does_not_gate_default_workflow_run(
self, app: Flask, monkeypatch: pytest.MonkeyPatch
) -> None:
workflow_module = sys.modules["controllers.service_api.app.workflow"]
monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", True)
billing_get_info = Mock(return_value={"enabled": True, "subscription": {"plan": CloudPlan.SANDBOX}})
generate = Mock(return_value={"result": "ok"})
monkeypatch.setattr(BillingService, "get_info", billing_get_info)
monkeypatch.setattr(AppGenerateService, "generate", generate)
api = WorkflowRunApi()
handler = unwrap(api.post)
with app.test_request_context("/workflows/run", method="POST", json={"inputs": {}}):
response = handler(
api,
session=Mock(),
app_model=_make_app_model(),
end_user=_make_end_user(),
)
assert response.get_json() == {"result": "ok"}
billing_get_info.assert_not_called()
generate.assert_called_once()
class TestWorkflowRunByIdApi:
def test_rejects_sandbox_plan_with_upgrade_error(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
workflow_module = sys.modules["controllers.service_api.app.workflow"]
monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", True)
billing_get_info = Mock(return_value={"enabled": True, "subscription": {"plan": CloudPlan.SANDBOX}})
generate = Mock()
monkeypatch.setattr(BillingService, "get_info", billing_get_info)
monkeypatch.setattr(AppGenerateService, "generate", generate)
api = WorkflowRunByIdApi()
handler = unwrap(api.post)
app_model = _make_app_model()
with app.test_request_context("/workflows/w1/run", method="POST", json={"inputs": {}}):
with pytest.raises(WorkflowVersionExecutionNotAllowedError) as exc_info:
handler(
api,
session=Mock(),
app_model=app_model,
end_user=_make_end_user(),
workflow_id="w1",
)
billing_get_info.assert_called_once_with(app_model.tenant_id, exclude_vector_space=True)
generate.assert_not_called()
assert exc_info.value.code == 403
assert exc_info.value.error_code == "workflow_version_execution_not_allowed"
assert exc_info.value.description == (
"Workflow version execution is not available on your current plan. Please upgrade to a paid plan."
)
assert exc_info.value.data == {
"code": "workflow_version_execution_not_allowed",
"message": exc_info.value.description,
"status": 403,
}
@pytest.mark.parametrize(
("billing_config_enabled", "billing_enabled", "plan"),
[
(False, True, CloudPlan.SANDBOX),
(True, False, CloudPlan.SANDBOX),
(True, True, CloudPlan.PROFESSIONAL),
],
)
def test_allows_execution_outside_enabled_sandbox_plan(
self,
app: Flask,
monkeypatch: pytest.MonkeyPatch,
billing_config_enabled: bool,
billing_enabled: bool,
plan: CloudPlan,
) -> None:
workflow_module = sys.modules["controllers.service_api.app.workflow"]
monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", billing_config_enabled)
billing_get_info = Mock(return_value={"enabled": billing_enabled, "subscription": {"plan": plan}})
generate = Mock(return_value={"result": "ok"})
monkeypatch.setattr(BillingService, "get_info", billing_get_info)
monkeypatch.setattr(AppGenerateService, "generate", generate)
api = WorkflowRunByIdApi()
handler = unwrap(api.post)
app_model = _make_app_model()
with app.test_request_context("/workflows/w1/run", method="POST", json={"inputs": {}}):
response = handler(
api,
session=Mock(),
app_model=app_model,
end_user=_make_end_user(),
workflow_id="w1",
)
assert response.get_json() == {"result": "ok"}
generate.assert_called_once()
if billing_config_enabled:
billing_get_info.assert_called_once_with(app_model.tenant_id, exclude_vector_space=True)
else:
billing_get_info.assert_not_called()
def test_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
workflow_module = sys.modules["controllers.service_api.app.workflow"]
monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", False)
monkeypatch.setattr(
AppGenerateService,
"generate",
@ -589,6 +699,8 @@ class TestWorkflowRunByIdApi:
handler(api, session=Mock(), app_model=app_model, end_user=end_user, workflow_id="w1")
def test_draft_workflow(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
workflow_module = sys.modules["controllers.service_api.app.workflow"]
monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", False)
monkeypatch.setattr(
AppGenerateService,
"generate",

View File

@ -28,6 +28,15 @@ describe('md.tsx components', () => {
expect(link).toHaveAttribute('href', '#get-messages')
})
it('should render a title action immediately after the title', () => {
render(<Heading {...defaultProps} titleAction={<button type="button">Upgrade</button>} />)
const heading = screen.getByRole('heading', { level: 2 })
expect(heading).toHaveClass('flex', 'items-center', 'gap-x-2')
expect(heading.children[0]).toBe(screen.getByRole('link', { name: 'Get Messages' }))
expect(heading.children[1]).toBe(screen.getByRole('button', { name: 'Upgrade' }))
})
it('should render an anchor span with correct id', () => {
const { container } = render(<Heading {...defaultProps} />)
const anchor = container.querySelector('#get-messages')

View File

@ -0,0 +1,129 @@
import { fireEvent, render, screen, within } from '@testing-library/react'
import { Plan } from '@/app/components/billing/type'
import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice'
let mockPlanType = Plan.professional
let mockEnableBilling = true
let mockIsFetchedPlan = true
vi.mock('@/context/provider-context', () => ({
useProviderContext: () => ({
plan: { type: mockPlanType },
enableBilling: mockEnableBilling,
isFetchedPlan: mockIsFetchedPlan,
}),
}))
vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({
PlanUpgradeModal: ({
show,
title,
description,
onClose,
}: {
show: boolean
title: string
description: string
onClose: () => void
}) =>
show ? (
<div data-testid="plan-upgrade-modal">
<div>{title}</div>
<div>{description}</div>
<button type="button" onClick={onClose}>
close upgrade modal
</button>
</div>
) : null,
}))
vi.mock('@/app/components/billing/upgrade-btn', () => ({
default: ({
onClick,
isShort,
size,
loc,
className,
}: {
onClick: () => void
isShort: boolean
size: string
loc: string
className: string
}) => (
<button
type="button"
data-is-short={isShort}
data-size={size}
data-loc={loc}
className={className}
onClick={onClick}
>
billing.upgradeBtn.encourageShort
</button>
),
}))
describe('WorkflowVersionApiUpgradeNotice', () => {
beforeEach(() => {
mockPlanType = Plan.professional
mockEnableBilling = true
mockIsFetchedPlan = true
})
it('should not render before the plan is fetched', () => {
mockPlanType = Plan.sandbox
mockIsFetchedPlan = false
render(<WorkflowVersionApiUpgradeNotice />)
expect(
screen.queryByRole('button', { name: 'billing.upgradeBtn.encourageShort' }),
).not.toBeInTheDocument()
})
it('should not render when billing is disabled', () => {
mockPlanType = Plan.sandbox
mockEnableBilling = false
render(<WorkflowVersionApiUpgradeNotice />)
expect(
screen.queryByRole('button', { name: 'billing.upgradeBtn.encourageShort' }),
).not.toBeInTheDocument()
})
it('should not render for paid plans', () => {
render(<WorkflowVersionApiUpgradeNotice />)
expect(
screen.queryByRole('button', { name: 'billing.upgradeBtn.encourageShort' }),
).not.toBeInTheDocument()
})
it('should show a small upgrade button and open the plan upgrade modal for sandbox plans', () => {
mockPlanType = Plan.sandbox
render(<WorkflowVersionApiUpgradeNotice />)
expect(screen.queryByText('billing.upgrade.workflowVersionRun.title')).not.toBeInTheDocument()
expect(
screen.queryByText('billing.upgrade.workflowVersionRun.description'),
).not.toBeInTheDocument()
const upgradeButton = screen.getByRole('button', { name: 'billing.upgradeBtn.encourageShort' })
expect(upgradeButton).toHaveAttribute('data-is-short', 'true')
expect(upgradeButton).toHaveAttribute('data-size', 'custom')
expect(upgradeButton).toHaveAttribute('data-loc', 'workflow-version-api-docs')
expect(upgradeButton).toHaveClass('h-5!', 'rounded-md!', 'px-1!')
fireEvent.click(upgradeButton)
const modal = screen.getByTestId('plan-upgrade-modal')
expect(modal).toBeInTheDocument()
expect(within(modal).getByText('billing.upgrade.workflowVersionRun.title')).toBeInTheDocument()
expect(
within(modal).getByText('billing.upgrade.workflowVersionRun.description'),
).toBeInTheDocument()
})
})

View File

@ -14,9 +14,10 @@ type IHeaderingProps = {
method: 'PUT' | 'DELETE' | 'GET' | 'POST' | 'PATCH'
title: string
name: string
titleAction?: React.ReactNode
}
export const Heading = function H2({ url, method, title, name }: IHeaderingProps) {
export const Heading = function H2({ url, method, title, name, titleAction }: IHeaderingProps) {
let style = ''
switch (method) {
case 'PUT':
@ -52,10 +53,11 @@ export const Heading = function H2({ url, method, title, name }: IHeaderingProps
{/* <span className="h-0.5 w-0.5 rounded-full bg-zinc-300 dark:bg-zinc-600"></span> */}
<span className="font-mono text-xs text-zinc-400">{url}</span>
</div>
<h2 className="mt-2 scroll-mt-32">
<h2 className={cn('mt-2 scroll-mt-32', titleAction && 'flex items-center gap-x-2')}>
<a href={name} className="group text-inherit no-underline hover:text-inherit">
{title}
</a>
{titleAction}
</h2>
</>
)

View File

@ -1,5 +1,6 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx'
# Workflow App API
@ -642,6 +643,7 @@ Workflow applications offers non-session support and is ideal for translation, a
method='POST'
title='Execute Specific Workflow'
name='#Execute-Specific-Workflow'
titleAction={<WorkflowVersionApiUpgradeNotice />}
/>
<Row>
<Col>
@ -823,6 +825,7 @@ Workflow applications offers non-session support and is ideal for translation, a
- 400, `draft_workflow_error`, Cannot use draft workflow version
- 400, `workflow_id_format_error`, Workflow ID format error, UUID format required
- 400, `workflow_request_error`, Workflow execution failed
- 403, `workflow_version_execution_not_allowed`, Workflow version execution is not available on your current plan. Please upgrade to a paid plan.
- 500, Internal service error
</Col>

View File

@ -1,5 +1,6 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx'
# ワークフローアプリ API
@ -642,6 +643,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
method='POST'
title='特定バージョンのワークフローを実行'
name='#Execute-Specific-Workflow'
titleAction={<WorkflowVersionApiUpgradeNotice />}
/>
<Row>
<Col>
@ -818,6 +820,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- 400`draft_workflow_error`,ドラフトワークフローバージョンを使用できません
- 400`workflow_id_format_error`ワークフローID形式エラー、UUID形式が必要です
- 400`workflow_request_error`,ワークフロー実行に失敗しました
- 403`workflow_version_execution_not_allowed`,現在のプランではワークフローバージョンを実行できません。有料プランにアップグレードしてください
- 500サービス内部異常
</Col>

View File

@ -1,5 +1,6 @@
import { CodeGroup } from '../code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx'
# Workflow 应用 API
@ -632,6 +633,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
method='POST'
title='执行指定版本 Workflow'
name='#Execute-Specific-Workflow'
titleAction={<WorkflowVersionApiUpgradeNotice />}
/>
<Row>
<Col>
@ -811,6 +813,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
- 400`draft_workflow_error`,无法使用草稿工作流版本
- 400`workflow_id_format_error`工作流ID格式错误需要UUID格式
- 400`workflow_request_error`workflow 执行失败
- 403`workflow_version_execution_not_allowed`,当前套餐不支持执行工作流版本,请升级至付费套餐
- 500服务内部异常
</Col>

View File

@ -0,0 +1,43 @@
'use client'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal'
import { Plan } from '@/app/components/billing/type'
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
import { useProviderContext } from '@/context/provider-context'
const WorkflowVersionApiUpgradeNotice = () => {
const { t } = useTranslation('billing')
const { plan, enableBilling, isFetchedPlan } = useProviderContext()
const [isPlanUpgradeModalOpen, setIsPlanUpgradeModalOpen] = useState(false)
if (!isFetchedPlan || !enableBilling || plan.type !== Plan.sandbox) return null
const title = t(($) => $['upgrade.workflowVersionRun.title'])
const description = t(($) => $['upgrade.workflowVersionRun.description'])
return (
<>
<span className="inline-flex">
<UpgradeBtn
isShort
size="custom"
loc="workflow-version-api-docs"
className="h-5! rounded-md! px-1!"
onClick={() => setIsPlanUpgradeModalOpen(true)}
/>
</span>
{isPlanUpgradeModalOpen && (
<PlanUpgradeModal
show
onClose={() => setIsPlanUpgradeModalOpen(false)}
title={title}
description={description}
/>
)}
</>
)
}
export default WorkflowVersionApiUpgradeNotice

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "قم بالترقية لتحميل عدة مستندات دفعة واحدة",
"upgrade.workflowRestore.description": "استعادة إصدارات سير العمل غير متاحة في خطتك الحالية.",
"upgrade.workflowRestore.title": "قم بالترقية لاستعادة إصدارات سير العمل",
"upgrade.workflowVersionRun.description": "تشغيل إصدار محدد من سير العمل باستخدام معرّف سير العمل غير متاح في خطتك الحالية.",
"upgrade.workflowVersionRun.title": "قم بالترقية لتشغيل إصدارات محددة من سير العمل",
"upgradeBtn.encourage": "الترقية الآن",
"upgradeBtn.encourageShort": "ترقية",
"upgradeBtn.plain": "عرض الخطة",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Upgrade, um mehrere Dokumente gleichzeitig hochzuladen",
"upgrade.workflowRestore.description": "Die Wiederherstellung von Workflow-Versionen ist in Ihrem aktuellen Tarif nicht verfügbar.",
"upgrade.workflowRestore.title": "Upgraden, um Workflow-Versionen wiederherzustellen",
"upgrade.workflowVersionRun.description": "Das Ausführen einer bestimmten Workflow-Version über die Workflow-ID ist in Ihrem aktuellen Tarif nicht verfügbar.",
"upgrade.workflowVersionRun.title": "Upgraden, um bestimmte Workflow-Versionen auszuführen",
"upgradeBtn.encourage": "Jetzt Upgraden",
"upgradeBtn.encourageShort": "Upgraden",
"upgradeBtn.plain": "Tarif Upgraden",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Upgrade to upload multiple documents at once",
"upgrade.workflowRestore.description": "Workflow version restoration is not available on your current plan.",
"upgrade.workflowRestore.title": "Upgrade to restore workflow versions",
"upgrade.workflowVersionRun.description": "Running a specific workflow version by workflow ID is not available on your current plan.",
"upgrade.workflowVersionRun.title": "Upgrade to run specific workflow versions",
"upgradeBtn.encourage": "Upgrade Now",
"upgradeBtn.encourageShort": "Upgrade",
"upgradeBtn.plain": "View Plan",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Actualiza para subir varios documentos a la vez",
"upgrade.workflowRestore.description": "La restauración de versiones del flujo de trabajo no está disponible en tu plan actual.",
"upgrade.workflowRestore.title": "Actualiza para restaurar versiones del flujo de trabajo",
"upgrade.workflowVersionRun.description": "Ejecutar una versión específica del flujo de trabajo mediante su ID no está disponible en tu plan actual.",
"upgrade.workflowVersionRun.title": "Actualiza para ejecutar versiones específicas del flujo de trabajo",
"upgradeBtn.encourage": "Actualizar Ahora",
"upgradeBtn.encourageShort": "Actualizar",
"upgradeBtn.plain": "Actualizar Plan",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "ارتقا برای آپلود همزمان چندین سند",
"upgrade.workflowRestore.description": "بازیابی نسخه‌های گردش‌کار در طرح فعلی شما در دسترس نیست.",
"upgrade.workflowRestore.title": "برای بازیابی نسخه‌های گردش‌کار ارتقا دهید",
"upgrade.workflowVersionRun.description": "اجرای نسخه‌ای خاص از گردش‌کار با شناسه گردش‌کار در طرح فعلی شما در دسترس نیست.",
"upgrade.workflowVersionRun.title": "برای اجرای نسخه‌های خاص گردش‌کار ارتقا دهید",
"upgradeBtn.encourage": "هم اکنون ارتقاء دهید",
"upgradeBtn.encourageShort": "ارتقاء دهید",
"upgradeBtn.plain": "ارتقاء طرح",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Passez à la version supérieure pour télécharger plusieurs documents à la fois",
"upgrade.workflowRestore.description": "La restauration des versions du workflow n'est pas disponible avec votre plan actuel.",
"upgrade.workflowRestore.title": "Mettez à niveau pour restaurer les versions du workflow",
"upgrade.workflowVersionRun.description": "Lexécution dune version spécifique du workflow via son ID nest pas disponible avec votre plan actuel.",
"upgrade.workflowVersionRun.title": "Mettez à niveau pour exécuter des versions spécifiques du workflow",
"upgradeBtn.encourage": "Mettre à niveau maintenant",
"upgradeBtn.encourageShort": "Mise à niveau",
"upgradeBtn.plain": "Mettre à jour le plan",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "एक बार में कई दस्तावेज़ अपलोड करने के लिए अपग्रेड करें",
"upgrade.workflowRestore.description": "आपकी वर्तमान योजना में वर्कफ़्लो संस्करण पुनर्स्थापना उपलब्ध नहीं है।",
"upgrade.workflowRestore.title": "वर्कफ़्लो संस्करणों को पुनर्स्थापित करने के लिए अपग्रेड करें",
"upgrade.workflowVersionRun.description": "वर्कफ़्लो ID के ज़रिए किसी विशिष्ट वर्कफ़्लो संस्करण को चलाना आपकी वर्तमान योजना में उपलब्ध नहीं है।",
"upgrade.workflowVersionRun.title": "विशिष्ट वर्कफ़्लो संस्करण चलाने के लिए अपग्रेड करें",
"upgradeBtn.encourage": "अभी अपग्रेड करें",
"upgradeBtn.encourageShort": "अपग्रेड करें",
"upgradeBtn.plain": "योजना अपग्रेड करें",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Tingkatkan untuk mengunggah beberapa dokumen sekaligus",
"upgrade.workflowRestore.description": "Pemulihan versi workflow tidak tersedia di paket Anda saat ini.",
"upgrade.workflowRestore.title": "Tingkatkan untuk memulihkan versi workflow",
"upgrade.workflowVersionRun.description": "Menjalankan versi workflow tertentu berdasarkan ID workflow tidak tersedia di paket Anda saat ini.",
"upgrade.workflowVersionRun.title": "Tingkatkan untuk menjalankan versi workflow tertentu",
"upgradeBtn.encourage": "Tingkatkan Sekarang",
"upgradeBtn.encourageShort": "Upgrade",
"upgradeBtn.plain": "Lihat Paket",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Aggiorna per caricare più documenti contemporaneamente",
"upgrade.workflowRestore.description": "Il ripristino delle versioni del workflow non è disponibile nel tuo piano attuale.",
"upgrade.workflowRestore.title": "Esegui l'upgrade per ripristinare le versioni del workflow",
"upgrade.workflowVersionRun.description": "Lesecuzione di una versione specifica del workflow tramite il relativo ID non è disponibile nel tuo piano attuale.",
"upgrade.workflowVersionRun.title": "Esegui lupgrade per avviare versioni specifiche del workflow",
"upgradeBtn.encourage": "Aggiorna Ora",
"upgradeBtn.encourageShort": "Aggiorna",
"upgradeBtn.plain": "Aggiorna Piano",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "複数ドキュメントを一度にアップロードするにはアップグレード",
"upgrade.workflowRestore.description": "現在のプランでは、ワークフローバージョンの復元は利用できません。",
"upgrade.workflowRestore.title": "アップグレードしてワークフローバージョンを復元",
"upgrade.workflowVersionRun.description": "現在のプランでは、ワークフロー ID を指定した特定バージョンの実行は利用できません。",
"upgrade.workflowVersionRun.title": "アップグレードして特定のワークフローバージョンを実行",
"upgradeBtn.encourage": "今すぐアップグレード",
"upgradeBtn.encourageShort": "アップグレード",
"upgradeBtn.plain": "プランを見る",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "한 번에 여러 문서를 업로드하려면 업그레이드하세요",
"upgrade.workflowRestore.description": "현재 플랜에서는 워크플로 버전 복원을 사용할 수 없습니다.",
"upgrade.workflowRestore.title": "워크플로 버전을 복원하려면 업그레이드하세요",
"upgrade.workflowVersionRun.description": "현재 플랜에서는 워크플로 ID로 특정 워크플로 버전을 실행할 수 없습니다.",
"upgrade.workflowVersionRun.title": "특정 워크플로 버전을 실행하려면 업그레이드하세요",
"upgradeBtn.encourage": "지금 업그레이드",
"upgradeBtn.encourageShort": "업그레이드",
"upgradeBtn.plain": "요금제 업그레이드",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Upgrade to upload multiple documents at once",
"upgrade.workflowRestore.description": "Het herstellen van workflowversies is niet beschikbaar in je huidige abonnement.",
"upgrade.workflowRestore.title": "Upgrade om workflowversies te herstellen",
"upgrade.workflowVersionRun.description": "Een specifieke workflowversie uitvoeren via de workflow-ID is niet beschikbaar in je huidige abonnement.",
"upgrade.workflowVersionRun.title": "Upgrade om specifieke workflowversies uit te voeren",
"upgradeBtn.encourage": "Upgrade Now",
"upgradeBtn.encourageShort": "Upgrade",
"upgradeBtn.plain": "View Plan",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Przejdź na wyższą wersję, aby przesyłać wiele dokumentów jednocześnie",
"upgrade.workflowRestore.description": "Przywracanie wersji przepływu pracy nie jest dostępne w obecnym planie.",
"upgrade.workflowRestore.title": "Ulepsz plan, aby przywracać wersje przepływu pracy",
"upgrade.workflowVersionRun.description": "Uruchamianie określonej wersji przepływu pracy za pomocą identyfikatora nie jest dostępne w obecnym planie.",
"upgrade.workflowVersionRun.title": "Ulepsz plan, aby uruchamiać określone wersje przepływu pracy",
"upgradeBtn.encourage": "Ulepsz teraz",
"upgradeBtn.encourageShort": "Ulepsz",
"upgradeBtn.plain": "Ulepsz plan",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Atualize para enviar vários documentos de uma vez",
"upgrade.workflowRestore.description": "A restauração de versões do fluxo de trabalho não está disponível no seu plano atual.",
"upgrade.workflowRestore.title": "Faça upgrade para restaurar versões do fluxo de trabalho",
"upgrade.workflowVersionRun.description": "Executar uma versão específica do fluxo de trabalho pelo ID não está disponível no seu plano atual.",
"upgrade.workflowVersionRun.title": "Faça upgrade para executar versões específicas do fluxo de trabalho",
"upgradeBtn.encourage": "Fazer Upgrade Agora",
"upgradeBtn.encourageShort": "Upgrade",
"upgradeBtn.plain": "Fazer Upgrade do Plano",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Actualizează pentru a încărca mai multe documente odată",
"upgrade.workflowRestore.description": "Restaurarea versiunilor fluxului de lucru nu este disponibilă în planul tău actual.",
"upgrade.workflowRestore.title": "Fă upgrade pentru a restaura versiunile fluxului de lucru",
"upgrade.workflowVersionRun.description": "Rularea unei versiuni specifice a fluxului de lucru prin ID nu este disponibilă în planul tău actual.",
"upgrade.workflowVersionRun.title": "Fă upgrade pentru a rula versiuni specifice ale fluxului de lucru",
"upgradeBtn.encourage": "Actualizează acum",
"upgradeBtn.encourageShort": "Actualizează",
"upgradeBtn.plain": "Actualizează planul",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Обновите версию, чтобы загружать несколько документов одновременно",
"upgrade.workflowRestore.description": "Восстановление версий рабочего процесса недоступно в вашем текущем плане.",
"upgrade.workflowRestore.title": "Обновите план, чтобы восстанавливать версии рабочего процесса",
"upgrade.workflowVersionRun.description": "Запуск определённой версии рабочего процесса по его идентификатору недоступен в вашем текущем плане.",
"upgrade.workflowVersionRun.title": "Обновите план, чтобы запускать определённые версии рабочего процесса",
"upgradeBtn.encourage": "Обновить сейчас",
"upgradeBtn.encourageShort": "Обновить",
"upgradeBtn.plain": "Обновить тарифный план",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Nadgradite za nalaganje več dokumentov hkrati",
"upgrade.workflowRestore.description": "Obnovitev različic poteka dela v vašem trenutnem paketu ni na voljo.",
"upgrade.workflowRestore.title": "Nadgradite za obnovitev različic poteka dela",
"upgrade.workflowVersionRun.description": "Zagon določene različice poteka dela prek njegovega ID-ja v vašem trenutnem paketu ni na voljo.",
"upgrade.workflowVersionRun.title": "Nadgradite za zagon določenih različic poteka dela",
"upgradeBtn.encourage": "Nadgradi zdaj",
"upgradeBtn.encourageShort": "Nadgradi",
"upgradeBtn.plain": "Nadgradi načrt",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "อัปเกรดเพื่ออัปโหลดเอกสารหลายฉบับพร้อมกัน",
"upgrade.workflowRestore.description": "การกู้คืนเวอร์ชันเวิร์กโฟลว์ไม่พร้อมใช้งานในแผนปัจจุบันของคุณ",
"upgrade.workflowRestore.title": "อัปเกรดเพื่อกู้คืนเวอร์ชันเวิร์กโฟลว์",
"upgrade.workflowVersionRun.description": "แผนปัจจุบันของคุณไม่รองรับการเรียกใช้เวอร์ชันเวิร์กโฟลว์ที่ระบุด้วย ID เวิร์กโฟลว์",
"upgrade.workflowVersionRun.title": "อัปเกรดเพื่อเรียกใช้เวอร์ชันเวิร์กโฟลว์ที่ระบุ",
"upgradeBtn.encourage": "อัปเกรดเดี๋ยวนี้",
"upgradeBtn.encourageShort": "อัพ เกรด",
"upgradeBtn.plain": "แผนการอัปเกรด",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Aynı anda birden fazla belge yüklemek için yükseltin",
"upgrade.workflowRestore.description": "İş akışı sürümü geri yükleme mevcut planınızda kullanılamaz.",
"upgrade.workflowRestore.title": "İş akışı sürümlerini geri yüklemek için yükseltin",
"upgrade.workflowVersionRun.description": "İş akışı kimliğiyle belirli bir iş akışı sürümünü çalıştırmak mevcut planınızda kullanılamaz.",
"upgrade.workflowVersionRun.title": "Belirli iş akışı sürümlerini çalıştırmak için yükseltin",
"upgradeBtn.encourage": "Şimdi Yükselt",
"upgradeBtn.encourageShort": "Yükselt",
"upgradeBtn.plain": "Planı Yükselt",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Оновіть, щоб завантажувати кілька документів одночасно",
"upgrade.workflowRestore.description": "Відновлення версій робочого процесу недоступне у вашому поточному плані.",
"upgrade.workflowRestore.title": "Оновіть план, щоб відновлювати версії робочого процесу",
"upgrade.workflowVersionRun.description": "Запуск певної версії робочого процесу за його ідентифікатором недоступний у вашому поточному плані.",
"upgrade.workflowVersionRun.title": "Оновіть план, щоб запускати певні версії робочого процесу",
"upgradeBtn.encourage": "Оновити зараз",
"upgradeBtn.encourageShort": "Оновити",
"upgradeBtn.plain": "Оновити план",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "Nâng cấp để tải lên nhiều tài liệu cùng lúc",
"upgrade.workflowRestore.description": "Tính năng khôi phục phiên bản quy trình không khả dụng trong gói hiện tại của bạn.",
"upgrade.workflowRestore.title": "Nâng cấp để khôi phục các phiên bản quy trình",
"upgrade.workflowVersionRun.description": "Gói hiện tại của bạn không hỗ trợ chạy một phiên bản quy trình cụ thể bằng ID quy trình.",
"upgrade.workflowVersionRun.title": "Nâng cấp để chạy các phiên bản quy trình cụ thể",
"upgradeBtn.encourage": "Nâng cấp Ngay",
"upgradeBtn.encourageShort": "Nâng cấp",
"upgradeBtn.plain": "Nâng cấp Kế hoạch",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "升级以一次性上传多个文档",
"upgrade.workflowRestore.description": "当前套餐不支持恢复工作流版本。",
"upgrade.workflowRestore.title": "升级以恢复工作流版本",
"upgrade.workflowVersionRun.description": "当前套餐不支持通过工作流 ID 执行指定版本。",
"upgrade.workflowVersionRun.title": "升级以执行指定工作流版本",
"upgradeBtn.encourage": "立即升级",
"upgradeBtn.encourageShort": "升级",
"upgradeBtn.plain": "查看套餐",

View File

@ -166,6 +166,8 @@
"upgrade.uploadMultiplePages.title": "升級以一次上傳多個文件",
"upgrade.workflowRestore.description": "目前方案不支援還原工作流程版本。",
"upgrade.workflowRestore.title": "升級以還原工作流程版本",
"upgrade.workflowVersionRun.description": "目前方案不支援透過工作流程 ID 執行指定版本。",
"upgrade.workflowVersionRun.title": "升級以執行指定工作流程版本",
"upgradeBtn.encourage": "立即升級",
"upgradeBtn.encourageShort": "升級",
"upgradeBtn.plain": "升級套餐",