mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 14:37:29 -06:00
Merge branch 'master' into feature_unify_pause_at_height
This commit is contained in:
commit
7ea3891da0
168 changed files with 15794 additions and 491 deletions
|
@ -1,23 +1,24 @@
|
|||
# Copyright (c) 2018 Jaime van Kessel, Ultimaker B.V.
|
||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from typing import Dict, Type, TYPE_CHECKING, List, Optional, cast
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Application import Application
|
||||
from UM.Extension import Extension
|
||||
from UM.Logger import Logger
|
||||
|
||||
import configparser # The script lists are stored in metadata as serialised config files.
|
||||
import importlib.util
|
||||
import io # To allow configparser to write to a string.
|
||||
import os.path
|
||||
import pkgutil
|
||||
import sys
|
||||
import importlib.util
|
||||
from typing import Dict, Type, TYPE_CHECKING, List, Optional, cast
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Extension import Extension
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Trust import Trust
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura import ApplicationMetadata
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
@ -161,7 +162,13 @@ class PostProcessingPlugin(QObject, Extension):
|
|||
# Iterate over all scripts.
|
||||
if script_name not in sys.modules:
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py"))
|
||||
file_path = os.path.join(path, script_name + ".py")
|
||||
if not self._isScriptAllowed(file_path):
|
||||
Logger.warning("Skipped loading post-processing script {}: not trusted".format(file_path))
|
||||
continue
|
||||
|
||||
spec = importlib.util.spec_from_file_location(__name__ + "." + script_name,
|
||||
file_path)
|
||||
loaded_script = importlib.util.module_from_spec(spec)
|
||||
if spec.loader is None:
|
||||
continue
|
||||
|
@ -334,4 +341,26 @@ class PostProcessingPlugin(QObject, Extension):
|
|||
if global_container_stack is not None:
|
||||
global_container_stack.propertyChanged.emit("post_processing_plugin", "value")
|
||||
|
||||
@staticmethod
|
||||
def _isScriptAllowed(file_path: str) -> bool:
|
||||
"""Checks whether the given file is allowed to be loaded"""
|
||||
if not ApplicationMetadata.IsEnterpriseVersion:
|
||||
# No signature needed
|
||||
return True
|
||||
|
||||
dir_path = os.path.split(file_path)[0] # type: str
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin")
|
||||
assert plugin_path is not None # appease mypy
|
||||
bundled_path = os.path.join(plugin_path, "scripts")
|
||||
if dir_path == bundled_path:
|
||||
# Bundled scripts are trusted.
|
||||
return True
|
||||
|
||||
trust_instance = Trust.getInstanceOrNone()
|
||||
if trust_instance is not None and Trust.signatureFileExistsFor(file_path):
|
||||
if trust_instance.signedFileCheck(file_path):
|
||||
return True
|
||||
|
||||
return False # Default verdict should be False, being the most secure fallback
|
||||
|
||||
|
||||
|
|
|
@ -57,6 +57,17 @@ class PauseAtHeight(Script):
|
|||
"options": {"marlin": "Marlin (M0)", "griffin": "Griffin (M0, firmware retract)", "bq": "BQ (M25)", "reprap": "RepRap (M226)", "repetier": "Repetier (@pause)"},
|
||||
"default_value": "marlin",
|
||||
"value": "\\\"griffin\\\" if machine_gcode_flavor==\\\"Griffin\\\" else \\\"reprap\\\" if machine_gcode_flavor==\\\"RepRap (RepRap)\\\" else \\\"repetier\\\" if machine_gcode_flavor==\\\"Repetier\\\" else \\\"bq\\\" if \\\"BQ\\\" in machine_name else \\\"marlin\\\""
|
||||
},
|
||||
"disarm_timeout":
|
||||
{
|
||||
"label": "Disarm timeout",
|
||||
"description": "After this time steppers are going to disarm (meaning that they can easily lose their positions). Set this to 0 if you don't want to set any duration.",
|
||||
"type": "int",
|
||||
"value": "0",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "1800",
|
||||
"unit": "s"
|
||||
},
|
||||
"head_park_x":
|
||||
{
|
||||
|
@ -206,6 +217,7 @@ class PauseAtHeight(Script):
|
|||
pause_at = self.getSettingValueByKey("pause_at")
|
||||
pause_height = self.getSettingValueByKey("pause_height")
|
||||
pause_layer = self.getSettingValueByKey("pause_layer")
|
||||
disarm_timeout = self.getSettingValueByKey("disarm_timeout")
|
||||
retraction_amount = self.getSettingValueByKey("retraction_amount")
|
||||
retraction_speed = self.getSettingValueByKey("retraction_speed")
|
||||
extrude_amount = self.getSettingValueByKey("extrude_amount")
|
||||
|
@ -393,6 +405,10 @@ class PauseAtHeight(Script):
|
|||
if display_text:
|
||||
prepend_gcode += "M117 " + display_text + "\n"
|
||||
|
||||
# Set the disarm timeout
|
||||
if disarm_timeout > 0:
|
||||
prepend_gcode += self.putValue(M = 18, S = disarm_timeout) + " ; Set the disarm timeout\n"
|
||||
|
||||
# Wait till the user continues printing
|
||||
prepend_gcode += pause_command + " ; Do the actual pause\n"
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Trust import Trust
|
||||
from ..PostProcessingPlugin import PostProcessingPlugin
|
||||
|
||||
# not sure if needed
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
|
||||
""" In this file, commnunity refers to regular Cura for makers."""
|
||||
|
||||
mock_plugin_registry = MagicMock()
|
||||
mock_plugin_registry.getPluginPath = MagicMock(return_value = "mocked_plugin_path")
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", False)
|
||||
def test_community_user_script_allowed():
|
||||
assert PostProcessingPlugin._isScriptAllowed("blaat.py")
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", False)
|
||||
def test_community_bundled_script_allowed():
|
||||
assert PostProcessingPlugin._isScriptAllowed(_bundled_file_path())
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", True)
|
||||
@patch.object(PluginRegistry, "getInstance", return_value=mock_plugin_registry)
|
||||
def test_enterprise_unsigned_user_script_not_allowed(plugin_registry):
|
||||
assert not PostProcessingPlugin._isScriptAllowed("blaat.py")
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", True)
|
||||
@patch.object(PluginRegistry, "getInstance", return_value=mock_plugin_registry)
|
||||
def test_enterprise_signed_user_script_allowed(plugin_registry):
|
||||
mocked_trust = MagicMock()
|
||||
mocked_trust.signedFileCheck = MagicMock(return_value=True)
|
||||
|
||||
plugin_registry.getPluginPath = MagicMock(return_value="mocked_plugin_path")
|
||||
|
||||
with patch.object(Trust, "signatureFileExistsFor", return_value = True):
|
||||
with patch("UM.Trust.Trust.getInstanceOrNone", return_value=mocked_trust):
|
||||
assert PostProcessingPlugin._isScriptAllowed("mocked_plugin_path/scripts/blaat.py")
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@patch("cura.ApplicationMetadata.IsEnterpriseVersion", False)
|
||||
def test_enterprise_bundled_script_allowed():
|
||||
assert PostProcessingPlugin._isScriptAllowed(_bundled_file_path())
|
||||
|
||||
|
||||
def _bundled_file_path():
|
||||
return os.path.join(
|
||||
Resources.getStoragePath(Resources.Resources) + "scripts/blaat.py"
|
||||
)
|
0
plugins/PostProcessingPlugin/tests/__init__.py
Normal file
0
plugins/PostProcessingPlugin/tests/__init__.py
Normal file
Loading…
Add table
Add a link
Reference in a new issue