mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-10 11:49:00 +08:00
feat: support Vectorizer can be used in workflow (#9932)
This commit is contained in:
parent
0ebd985672
commit
ddb960ddfb
@ -165,6 +165,12 @@ class BaseAgentRunner(AppRunner):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
parameter_type = parameter.type.as_normal_type()
|
parameter_type = parameter.type.as_normal_type()
|
||||||
|
if parameter.type in {
|
||||||
|
ToolParameter.ToolParameterType.SYSTEM_FILES,
|
||||||
|
ToolParameter.ToolParameterType.FILE,
|
||||||
|
ToolParameter.ToolParameterType.FILES,
|
||||||
|
}:
|
||||||
|
continue
|
||||||
enum = []
|
enum = []
|
||||||
if parameter.type == ToolParameter.ToolParameterType.SELECT:
|
if parameter.type == ToolParameter.ToolParameterType.SELECT:
|
||||||
enum = [option.value for option in parameter.options]
|
enum = [option.value for option in parameter.options]
|
||||||
@ -250,6 +256,12 @@ class BaseAgentRunner(AppRunner):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
parameter_type = parameter.type.as_normal_type()
|
parameter_type = parameter.type.as_normal_type()
|
||||||
|
if parameter.type in {
|
||||||
|
ToolParameter.ToolParameterType.SYSTEM_FILES,
|
||||||
|
ToolParameter.ToolParameterType.FILE,
|
||||||
|
ToolParameter.ToolParameterType.FILES,
|
||||||
|
}:
|
||||||
|
continue
|
||||||
enum = []
|
enum = []
|
||||||
if parameter.type == ToolParameter.ToolParameterType.SELECT:
|
if parameter.type == ToolParameter.ToolParameterType.SELECT:
|
||||||
enum = [option.value for option in parameter.options]
|
enum = [option.value for option in parameter.options]
|
||||||
|
@ -76,8 +76,16 @@ def to_prompt_message_content(f: File, /):
|
|||||||
|
|
||||||
|
|
||||||
def download(f: File, /):
|
def download(f: File, /):
|
||||||
|
if f.transfer_method == FileTransferMethod.TOOL_FILE:
|
||||||
|
tool_file = file_repository.get_tool_file(session=db.session(), file=f)
|
||||||
|
return _download_file_content(tool_file.file_key)
|
||||||
|
elif f.transfer_method == FileTransferMethod.LOCAL_FILE:
|
||||||
upload_file = file_repository.get_upload_file(session=db.session(), file=f)
|
upload_file = file_repository.get_upload_file(session=db.session(), file=f)
|
||||||
return _download_file_content(upload_file.key)
|
return _download_file_content(upload_file.key)
|
||||||
|
# remote file
|
||||||
|
response = ssrf_proxy.get(f.remote_url, follow_redirects=True)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.content
|
||||||
|
|
||||||
|
|
||||||
def _download_file_content(path: str, /):
|
def _download_file_content(path: str, /):
|
||||||
|
@ -1 +0,0 @@
|
|||||||
VECTORIZER_ICON_PNG = "iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAACxLAAAsSwGlPZapAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAboSURBVHgB7Z09bBxFFMffRoAvcQqbguBUxu4wCUikMCZ0TmQK4NLQJCJOlQIkokgEGhQ7NCFIKEhQuIqNnIaGMxRY2GVwmlggDHS+pIHELmIXMTEULPP3eeXz7e7szO7MvE1ufpKV03nuNn7/mfcxH7tEHo/H42lXgqwG1bGw65+/aTQM6K0gpJdCoi7ypCIMui5s9Qv9R1OVTqrVxoL1jPbpvH4hrIp/rnmj5+YOhTQ++1kwmdZgT9ovRi6EF4Xhv/XGL0Sv6OLXYMu0BokjYOSDcBQfJI8xhKFP/HAlqCW8v5vqubBr8yn6maCexxiIDR376LnWmBBzQZtPEvx+L3mMAleOZKb1/XgM2EOnyWMFZJKt78UEQKpJHisk2TYmgM967JFk2z3kYcULwIwXgBkvADNeAGa8AMw8Qcwc6N55/eAh0cYmGaOzQtR/kOhQX+M6+/c23r+3RlT/i2ipTrSyRqw4F+CwMMbgANHQwG7jRywLw/wqDDNzI79xYPjqa2L262jjtYzaT0QT3xEbsck4MXUakgWOvUx08liy0ZPYEKNhel4Y6AZpgR7/8Tvq1wEQ+sMJN6Nh9kqwy+bWYwAM8elZovNv6xmlU7iLs280RNO9ls51os/h/8eBVQEig8Dt5OXUsNrno2tluZw0cI3qUXKONQHy9sYkVHqnjntLA2LnFTAv1gSA+zBhfIDvkfVO/B4xRgWZn4fbe2WAnGJFAAxn03+I7PtUXdzE90Sjl4ne+6L4d5nCigAyYyHPn7tFdPN30uJwX/qI6jtISkQZFVLdhd9SrtNPTrFSB6QZBAaYntsptpAyfvk+KYOCamVR/XrNtLqepduiFnkh3g4iIw6YLAhlOJmKwB9zaarhApr/MPREjAZVisSU1s/KYsGzhmKXClYEWLm/8xpV7btXhcv5I7lt2vtJFA3q/T07r1HopdG5l5xhxQVdn28YFn8kBJCBOZmiPHio1m5QuJzlu9ntXApgZwSsNYJslvGjtjrfm8Sq4neceFUtz3dZCzwW09Gqo2hreuPN7HZRnNqa1BP1x8lhczVNK+zT0TqkjYAF4e7Okxoo2PZX5K4IrhNpb/P8FTK2S1+TcUq1HpBFmquJYo1qEYU6RVarJE0c2ooL7C5IRwBZ5nJ9joyRtk5hA3YBdHqWzG1gBKgE/bzMaK5LqMIugKrbUDHu59/YWVRBsWhrsYZdANV5HBUXYGNlC9dFBW8LdgH6FQVYUnQvkQgm3NH8YuO7bM4LsWZBfT3qRY9OxRyJgJRz+Ij+FDPEQ1C3GVMiWAVQ7f31u/ncytxi4wdZTbRGgdcHnpYLD/FcwSrAoOKizfKfVAiIF4kBMPK+Opfe1iWsMUB1BJh2BRgBabSNAOiFqkXYbcNFUF9P+u82FGdWTcEmgGrvh0FUppB1kC073muXEaDq/21kIjLxV9tFAC7/n5X6tkUM0PH/dcP+P0v41fvkFBYBVHs/MD0CDmVsOzEdb7JgEYDT/8uq4rpj44NSjwDTc/CyzV1gxbH7Ac4F0PH/S4ZHAOaFZLiY+2nFuQA6/t9kQMTCz1CG66tbWvWS4VwAVf9vugAbel6efqrsYbKBcwFeVNz8ajobyTppw2F84FQAnfl/kwER6wJZcWdBc7e2KZwKoOP/TVakWb0f7md+kVhwOwI0BDCFyq42rt4PSiuAiRGAEXdK4ZQlV+8HTgVwefwHvR7nhbOA0FwBGDgTIM/Z3SLXUj2hOW1wR10eSrs7Ou9eTB3jo/dzuh/gTABdn35c8dhpM3BxOmeTuXs/cDoCdDY4qe7l32pbaZxL1jF+GXo/cLotBcWVTiZU3T7RMn8rHiijW9FgauP4Ef1TLdhHWgacCgAj6tYCqGKjU/DNbqxIkMYZNs7MpxmnLuhmwYJna1dbdzHjY42hDL4/wqkA6HWuDkAngRH0iYVjRkVwnoZO/0gsuLwpkw7OBcAtwlwvfESHxctmfMBSiOG0oStj4HCF7T3+RWARwIU7QK/HbWlqls52mYJtezqMj3v34C5VOveFy8Ll4QoTsJ8Txp0RsW8/Os2im2LCtSC1RIqLw3RldTVplOKkPEYDhMAPqttnune2rzTv5Y+WKdEem2ixkWqZYSeDSUp3qwIYNOrR7cBjcbOORxkvADNeAGa8AMx4AZjxAjATf5Ab0Tp5rJBk2/iD3PAwYo8Vkmyb9CjDGfLYIaCp1rdiAnT8S5PeDVkgoDuVCsWeJxwToHZ163m3Z8hjloDGk54vn5gFbT/5eZw8phifvZz8XPlA9qmRj8JRCumi+OkljzbbrvxM0qPMm9rIqY6FXZubVBUinMbzcP3jbuXA6Mh2kMx07KPJJLfj8Xg8Hg/4H+KfFYb2WM4MAAAAAElFTkSuQmCC" # noqa: E501
|
|
@ -1,11 +1,12 @@
|
|||||||
from base64 import b64decode
|
|
||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
from httpx import post
|
from httpx import post
|
||||||
|
|
||||||
|
from core.file.enums import FileType
|
||||||
|
from core.file.file_manager import download
|
||||||
|
from core.tools.entities.common_entities import I18nObject
|
||||||
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
|
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter
|
||||||
from core.tools.errors import ToolProviderCredentialValidationError
|
from core.tools.errors import ToolParameterValidationError
|
||||||
from core.tools.provider.builtin.vectorizer.tools.test_data import VECTORIZER_ICON_PNG
|
|
||||||
from core.tools.tool.builtin_tool import BuiltinTool
|
from core.tools.tool.builtin_tool import BuiltinTool
|
||||||
|
|
||||||
|
|
||||||
@ -16,30 +17,30 @@ class VectorizerTool(BuiltinTool):
|
|||||||
"""
|
"""
|
||||||
invoke tools
|
invoke tools
|
||||||
"""
|
"""
|
||||||
api_key_name = self.runtime.credentials.get("api_key_name", None)
|
api_key_name = self.runtime.credentials.get("api_key_name")
|
||||||
api_key_value = self.runtime.credentials.get("api_key_value", None)
|
api_key_value = self.runtime.credentials.get("api_key_value")
|
||||||
mode = tool_parameters.get("mode", "test")
|
mode = tool_parameters.get("mode", "test")
|
||||||
if mode == "production":
|
|
||||||
mode = "preview"
|
|
||||||
|
|
||||||
if not api_key_name or not api_key_value:
|
|
||||||
raise ToolProviderCredentialValidationError("Please input api key name and value")
|
|
||||||
|
|
||||||
|
# image file for workflow mode
|
||||||
|
image = tool_parameters.get("image")
|
||||||
|
if image and image.type != FileType.IMAGE:
|
||||||
|
raise ToolParameterValidationError("Not a valid image")
|
||||||
|
# image_id for agent mode
|
||||||
image_id = tool_parameters.get("image_id", "")
|
image_id = tool_parameters.get("image_id", "")
|
||||||
if not image_id:
|
|
||||||
return self.create_text_message("Please input image id")
|
|
||||||
|
|
||||||
if image_id.startswith("__test_"):
|
if image_id:
|
||||||
image_binary = b64decode(VECTORIZER_ICON_PNG)
|
|
||||||
else:
|
|
||||||
image_binary = self.get_variable_file(self.VariableKey.IMAGE)
|
image_binary = self.get_variable_file(self.VariableKey.IMAGE)
|
||||||
if not image_binary:
|
if not image_binary:
|
||||||
return self.create_text_message("Image not found, please request user to generate image firstly.")
|
return self.create_text_message("Image not found, please request user to generate image firstly.")
|
||||||
|
elif image:
|
||||||
|
image_binary = download(image)
|
||||||
|
else:
|
||||||
|
raise ToolParameterValidationError("Please provide either image or image_id")
|
||||||
|
|
||||||
response = post(
|
response = post(
|
||||||
"https://vectorizer.ai/api/v1/vectorize",
|
"https://vectorizer.ai/api/v1/vectorize",
|
||||||
|
data={"mode": mode},
|
||||||
files={"image": image_binary},
|
files={"image": image_binary},
|
||||||
data={"mode": mode} if mode == "test" else {},
|
|
||||||
auth=(api_key_name, api_key_value),
|
auth=(api_key_name, api_key_value),
|
||||||
timeout=30,
|
timeout=30,
|
||||||
)
|
)
|
||||||
@ -59,11 +60,23 @@ class VectorizerTool(BuiltinTool):
|
|||||||
return [
|
return [
|
||||||
ToolParameter.get_simple_instance(
|
ToolParameter.get_simple_instance(
|
||||||
name="image_id",
|
name="image_id",
|
||||||
llm_description=f"the image id that you want to vectorize, \
|
llm_description=f"the image_id that you want to vectorize, \
|
||||||
and the image id should be specified in \
|
and the image_id should be specified in \
|
||||||
{[i.name for i in self.list_default_image_variables()]}",
|
{[i.name for i in self.list_default_image_variables()]}",
|
||||||
type=ToolParameter.ToolParameterType.SELECT,
|
type=ToolParameter.ToolParameterType.SELECT,
|
||||||
required=True,
|
required=False,
|
||||||
options=[i.name for i in self.list_default_image_variables()],
|
options=[i.name for i in self.list_default_image_variables()],
|
||||||
)
|
),
|
||||||
|
ToolParameter(
|
||||||
|
name="image",
|
||||||
|
label=I18nObject(en_US="image", zh_Hans="image"),
|
||||||
|
human_description=I18nObject(
|
||||||
|
en_US="The image to be converted.",
|
||||||
|
zh_Hans="要转换的图片。",
|
||||||
|
),
|
||||||
|
type=ToolParameter.ToolParameterType.FILE,
|
||||||
|
form=ToolParameter.ToolParameterForm.LLM,
|
||||||
|
llm_description="you should not input this parameter. just input the image_id.",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -4,14 +4,21 @@ identity:
|
|||||||
label:
|
label:
|
||||||
en_US: Vectorizer.AI
|
en_US: Vectorizer.AI
|
||||||
zh_Hans: Vectorizer.AI
|
zh_Hans: Vectorizer.AI
|
||||||
pt_BR: Vectorizer.AI
|
|
||||||
description:
|
description:
|
||||||
human:
|
human:
|
||||||
en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
|
en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
|
||||||
zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
|
zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
|
||||||
pt_BR: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
|
|
||||||
llm: A tool for converting images to SVG vectors. you should input the image id as the input of this tool. the image id can be got from parameters.
|
llm: A tool for converting images to SVG vectors. you should input the image id as the input of this tool. the image id can be got from parameters.
|
||||||
parameters:
|
parameters:
|
||||||
|
- name: image
|
||||||
|
type: file
|
||||||
|
label:
|
||||||
|
en_US: image
|
||||||
|
human_description:
|
||||||
|
en_US: The image to be converted.
|
||||||
|
zh_Hans: 要转换的图片。
|
||||||
|
llm_description: you should not input this parameter. just input the image_id.
|
||||||
|
form: llm
|
||||||
- name: mode
|
- name: mode
|
||||||
type: select
|
type: select
|
||||||
required: true
|
required: true
|
||||||
@ -20,19 +27,15 @@ parameters:
|
|||||||
label:
|
label:
|
||||||
en_US: production
|
en_US: production
|
||||||
zh_Hans: 生产模式
|
zh_Hans: 生产模式
|
||||||
pt_BR: production
|
|
||||||
- value: test
|
- value: test
|
||||||
label:
|
label:
|
||||||
en_US: test
|
en_US: test
|
||||||
zh_Hans: 测试模式
|
zh_Hans: 测试模式
|
||||||
pt_BR: test
|
|
||||||
default: test
|
default: test
|
||||||
label:
|
label:
|
||||||
en_US: Mode
|
en_US: Mode
|
||||||
zh_Hans: 模式
|
zh_Hans: 模式
|
||||||
pt_BR: Mode
|
|
||||||
human_description:
|
human_description:
|
||||||
en_US: It is free to integrate with and test out the API in test mode, no subscription required.
|
en_US: It is free to integrate with and test out the API in test mode, no subscription required.
|
||||||
zh_Hans: 在测试模式下,可以免费测试API。
|
zh_Hans: 在测试模式下,可以免费测试API。
|
||||||
pt_BR: It is free to integrate with and test out the API in test mode, no subscription required.
|
|
||||||
form: form
|
form: form
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from core.file import File
|
||||||
|
from core.file.enums import FileTransferMethod, FileType
|
||||||
from core.tools.errors import ToolProviderCredentialValidationError
|
from core.tools.errors import ToolProviderCredentialValidationError
|
||||||
from core.tools.provider.builtin.vectorizer.tools.vectorizer import VectorizerTool
|
from core.tools.provider.builtin.vectorizer.tools.vectorizer import VectorizerTool
|
||||||
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
|
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
|
||||||
@ -7,6 +9,12 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
|
|||||||
|
|
||||||
class VectorizerProvider(BuiltinToolProviderController):
|
class VectorizerProvider(BuiltinToolProviderController):
|
||||||
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
|
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
|
||||||
|
test_img = File(
|
||||||
|
tenant_id="__test_123",
|
||||||
|
remote_url="https://cloud.dify.ai/logo/logo-site.png",
|
||||||
|
type=FileType.IMAGE,
|
||||||
|
transfer_method=FileTransferMethod.REMOTE_URL,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
VectorizerTool().fork_tool_runtime(
|
VectorizerTool().fork_tool_runtime(
|
||||||
runtime={
|
runtime={
|
||||||
@ -14,7 +22,7 @@ class VectorizerProvider(BuiltinToolProviderController):
|
|||||||
}
|
}
|
||||||
).invoke(
|
).invoke(
|
||||||
user_id="",
|
user_id="",
|
||||||
tool_parameters={"mode": "test", "image_id": "__test_123"},
|
tool_parameters={"mode": "test", "image": test_img},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ToolProviderCredentialValidationError(str(e))
|
raise ToolProviderCredentialValidationError(str(e))
|
||||||
|
@ -4,11 +4,9 @@ identity:
|
|||||||
label:
|
label:
|
||||||
en_US: Vectorizer.AI
|
en_US: Vectorizer.AI
|
||||||
zh_Hans: Vectorizer.AI
|
zh_Hans: Vectorizer.AI
|
||||||
pt_BR: Vectorizer.AI
|
|
||||||
description:
|
description:
|
||||||
en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
|
en_US: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
|
||||||
zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
|
zh_Hans: 一个将 PNG 和 JPG 图像快速轻松地转换为 SVG 矢量图的工具。
|
||||||
pt_BR: Convert your PNG and JPG images to SVG vectors quickly and easily. Fully automatically. Using AI.
|
|
||||||
icon: icon.png
|
icon: icon.png
|
||||||
tags:
|
tags:
|
||||||
- productivity
|
- productivity
|
||||||
@ -20,15 +18,12 @@ credentials_for_provider:
|
|||||||
label:
|
label:
|
||||||
en_US: Vectorizer.AI API Key name
|
en_US: Vectorizer.AI API Key name
|
||||||
zh_Hans: Vectorizer.AI API Key name
|
zh_Hans: Vectorizer.AI API Key name
|
||||||
pt_BR: Vectorizer.AI API Key name
|
|
||||||
placeholder:
|
placeholder:
|
||||||
en_US: Please input your Vectorizer.AI ApiKey name
|
en_US: Please input your Vectorizer.AI ApiKey name
|
||||||
zh_Hans: 请输入你的 Vectorizer.AI ApiKey name
|
zh_Hans: 请输入你的 Vectorizer.AI ApiKey name
|
||||||
pt_BR: Please input your Vectorizer.AI ApiKey name
|
|
||||||
help:
|
help:
|
||||||
en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
|
en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
|
||||||
zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
|
zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
|
||||||
pt_BR: Get your Vectorizer.AI API Key from Vectorizer.AI.
|
|
||||||
url: https://vectorizer.ai/api
|
url: https://vectorizer.ai/api
|
||||||
api_key_value:
|
api_key_value:
|
||||||
type: secret-input
|
type: secret-input
|
||||||
@ -36,12 +31,9 @@ credentials_for_provider:
|
|||||||
label:
|
label:
|
||||||
en_US: Vectorizer.AI API Key
|
en_US: Vectorizer.AI API Key
|
||||||
zh_Hans: Vectorizer.AI API Key
|
zh_Hans: Vectorizer.AI API Key
|
||||||
pt_BR: Vectorizer.AI API Key
|
|
||||||
placeholder:
|
placeholder:
|
||||||
en_US: Please input your Vectorizer.AI ApiKey
|
en_US: Please input your Vectorizer.AI ApiKey
|
||||||
zh_Hans: 请输入你的 Vectorizer.AI ApiKey
|
zh_Hans: 请输入你的 Vectorizer.AI ApiKey
|
||||||
pt_BR: Please input your Vectorizer.AI ApiKey
|
|
||||||
help:
|
help:
|
||||||
en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
|
en_US: Get your Vectorizer.AI API Key from Vectorizer.AI.
|
||||||
zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
|
zh_Hans: 从 Vectorizer.AI 获取您的 Vectorizer.AI API Key。
|
||||||
pt_BR: Get your Vectorizer.AI API Key from Vectorizer.AI.
|
|
||||||
|
@ -242,11 +242,15 @@ class ToolManager:
|
|||||||
parameters = tool_entity.get_all_runtime_parameters()
|
parameters = tool_entity.get_all_runtime_parameters()
|
||||||
for parameter in parameters:
|
for parameter in parameters:
|
||||||
# check file types
|
# check file types
|
||||||
if parameter.type in {
|
if (
|
||||||
|
parameter.type
|
||||||
|
in {
|
||||||
ToolParameter.ToolParameterType.SYSTEM_FILES,
|
ToolParameter.ToolParameterType.SYSTEM_FILES,
|
||||||
ToolParameter.ToolParameterType.FILE,
|
ToolParameter.ToolParameterType.FILE,
|
||||||
ToolParameter.ToolParameterType.FILES,
|
ToolParameter.ToolParameterType.FILES,
|
||||||
}:
|
}
|
||||||
|
and parameter.required
|
||||||
|
):
|
||||||
raise ValueError(f"file type parameter {parameter.name} not supported in agent")
|
raise ValueError(f"file type parameter {parameter.name} not supported in agent")
|
||||||
|
|
||||||
if parameter.form == ToolParameter.ToolParameterForm.FORM:
|
if parameter.form == ToolParameter.ToolParameterForm.FORM:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user