Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAYA-110727 - Crash when editing Layer That has Permission to Edit false (locked) #1363

Merged
merged 6 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/mayaUsd/ufe/UsdTransform3dHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "UsdTransform3dHandler.h"

#include <mayaUsd/ufe/UsdSceneItem.h>
#include <mayaUsd/ufe/Utils.h>

#include <maya/MGlobal.h>

Expand Down Expand Up @@ -72,6 +73,12 @@ Ufe::Transform3d::Ptr UsdTransform3dHandler::transform3d(const Ufe::SceneItem::P
return nullptr;
}

std::string errMsg;
if (!MayaUsd::ufe::isEditTargetLayerModifiable(usdItem->prim().GetStage(), &errMsg)) {
MGlobal::displayError(errMsg.c_str());
return nullptr;
}

return UsdTransform3d::create(usdItem);
}

Expand Down
11 changes: 4 additions & 7 deletions lib/mayaUsd/ufe/UsdTransform3dMayaXformStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -909,13 +909,10 @@ Ufe::Transform3d::Ptr UsdTransform3dMayaXformStackHandler::editTransform3d(
return nullptr;
}

auto stage = usdItem->prim().GetStage();
if (stage) {
const SdfLayerHandle& editLayer = stage->GetEditTarget().GetLayer();
if (editLayer && stage->IsLayerMuted(editLayer->GetIdentifier())) {
MGlobal::displayError("Editing a muted layer is not allowed.");
return nullptr;
}
std::string errMsg;
if (!MayaUsd::ufe::isEditTargetLayerModifiable(usdItem->prim().GetStage(), &errMsg)) {
MGlobal::displayError(errMsg.c_str());
return nullptr;
}

return createTransform3d(
Expand Down
13 changes: 9 additions & 4 deletions lib/mayaUsd/ufe/UsdUndoAddNewPrimCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,15 @@ void UsdUndoAddNewPrimCommand::execute()
UsdUndoBlock undoBlock(&_undoableItem);

if (_stage) {
MayaUsd::ufe::InAddOrDeleteOperation ad;
auto prim = _stage->DefinePrim(_primPath, _primToken);
if (!prim.IsValid())
TF_RUNTIME_ERROR("Failed to create new prim type: %s", _primToken.GetText());
std::string errMsg;
if (!MayaUsd::ufe::isEditTargetLayerModifiable(_stage, &errMsg)) {
TF_RUNTIME_ERROR("%s", errMsg.c_str());
} else {
MayaUsd::ufe::InAddOrDeleteOperation ad;
auto prim = _stage->DefinePrim(_primPath, _primToken);
if (!prim.IsValid())
TF_RUNTIME_ERROR("Failed to create new prim type: %s", _primToken.GetText());
}
}
}

Expand Down
36 changes: 36 additions & 0 deletions lib/mayaUsd/ufe/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ bool isAttributeEditAllowed(const PXR_NS::UsdAttribute& attr, std::string* errMs
const auto& stage = prim.GetStage();
const auto& editTarget = stage->GetEditTarget();

if (!isEditTargetLayerModifiable(stage, errMsg)) {
return false;
}

// get the index to edit target layer
const auto targetLayerIndex = findLayerIndex(prim, editTarget.GetLayer());

Expand Down Expand Up @@ -443,6 +447,38 @@ bool isAttributeEditAllowed(const UsdPrim& prim, const TfToken& attrName)
return true;
}

bool isEditTargetLayerModifiable(const PXR_NS::UsdStageWeakPtr stage, std::string* errMsg)
{
const auto editTarget = stage->GetEditTarget();
const auto editLayer = editTarget.GetLayer();

if (editLayer && !editLayer->PermissionToEdit()) {
if (errMsg) {
std::string err = TfStringPrintf(
"Cannot edit [%s] because it is read-only. Set PermissionToEdit = true to proceed.",
editLayer->GetDisplayName().c_str());

*errMsg = err;
}

return false;
}

if (stage->IsLayerMuted(editLayer->GetIdentifier())) {
if (errMsg) {
std::string err = TfStringPrintf(
"Cannot edit [%s] because it is muted. Unmute [%s] to proceed.",
editLayer->GetDisplayName().c_str(),
editLayer->GetDisplayName().c_str());
*errMsg = err;
}

return false;
}

return true;
}

Ufe::Selection removeDescendants(const Ufe::Selection& src, const Ufe::Path& filterPath)
{
// Filter the src selection, removing items below the filterPath
Expand Down
7 changes: 7 additions & 0 deletions lib/mayaUsd/ufe/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ bool isAttributeEditAllowed(const PXR_NS::UsdAttribute& attr, std::string* errMs
MAYAUSD_CORE_PUBLIC
bool isAttributeEditAllowed(const PXR_NS::UsdPrim& prim, const PXR_NS::TfToken& attrName);

//! Check if the edit target in the stage is allowed to be changed.
//! \return True, if the edit target layer in the stage is allowed to be changed
MAYAUSD_CORE_PUBLIC
bool isEditTargetLayerModifiable(
const PXR_NS::UsdStageWeakPtr stage,
std::string* errMsg = nullptr);

//! Send notification for data model changes
template <class T>
void sendNotification(const Ufe::SceneItem::Ptr& item, const Ufe::Path& previousPath)
Expand Down
6 changes: 6 additions & 0 deletions lib/mayaUsd/ufe/wrapUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ bool isAttributeEditAllowed(const PXR_NS::UsdAttribute& attr)
return ufe::isAttributeEditAllowed(attr);
}

bool isEditTargetLayerModifiable(const PXR_NS::UsdStageWeakPtr stage)
{
return ufe::isEditTargetLayerModifiable(stage);
}

PXR_NS::TfTokenVector getProxyShapePurposes(const std::string& ufePathString)
{
auto path =
Expand Down Expand Up @@ -236,4 +241,5 @@ void wrapUtils()
def("ufePathToInstanceIndex", ufePathToInstanceIndex);
def("getProxyShapePurposes", getProxyShapePurposes);
def("isAttributeEditAllowed", isAttributeEditAllowed);
def("isEditTargetLayerModifiable", isEditTargetLayerModifiable);
}
1 change: 1 addition & 0 deletions test/lib/ufe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(TEST_SCRIPT_FILES
testSelection.py
testUfePythonImport.py
testAttributeBlock.py
testBlockedLayerEdit.py
)

set(TEST_SUPPORT_FILES
Expand Down
165 changes: 165 additions & 0 deletions test/lib/ufe/testBlockedLayerEdit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env python

#
# Copyright 2021 Autodesk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import fixturesUtils
import mayaUtils
import usdUtils

from pxr import Sdf

from maya import cmds
from maya import standalone

import mayaUsd
from mayaUsd import ufe as mayaUsdUfe

import ufe
import unittest

class BlockedLayerEditTestCase(unittest.TestCase):

pluginsLoaded = False

@classmethod
def setUpClass(cls):
fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False)

if not cls.pluginsLoaded:
cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

@classmethod
def tearDownClass(cls):
standalone.uninitialize()

def setUp(self):
''' Called initially to set up the Maya test environment '''
# Load plugins
self.assertTrue(self.pluginsLoaded)

# Clear selection to start off
cmds.select(clear=True)

def testTranslateMutedLayer(self):
# create new stage
cmds.file(new=True, force=True)

# Open usdCylinder.ma scene in testSamples
mayaUtils.openCylinderScene()

# get the stage
proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
proxyShapePath = proxyShapes[0]
stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

# cylinder prim
cylinderPrim = stage.GetPrimAtPath('/pCylinder1')
self.assertIsNotNone(cylinderPrim)

# create a sub-layer.
rootLayer = stage.GetRootLayer()
subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="LayerA")[0]

# check to see the if the sublayers was added
addedLayers = [subLayerA]
self.assertEqual(rootLayer.subLayerPaths, addedLayers)

# set the edit target to LayerA.
cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerA)

# mute the layer
stage.MuteLayer(subLayerA)

# Check that our helper function is returning the right thing
self.assertFalse(mayaUsdUfe.isEditTargetLayerModifiable(stage))

# Get translate attribute and make sure its empty
translateAttr = cylinderPrim.GetAttribute('xformOp:translate')
self.assertIsNotNone(translateAttr)

tranlateBeforeEdit = translateAttr.Get()

# create a transform3d and translate
cylinderPath = ufe.Path([
mayaUtils.createUfePathSegment("|mayaUsdTransform|shape"),
usdUtils.createUfePathSegment("/pCylinder1")])
cylinderItem = ufe.Hierarchy.createItem(cylinderPath)

cylinderT3d = ufe.Transform3d.transform3d(cylinderItem)
if cylinderT3d is not None:
cylinderT3d.translate(5.0, 6.0, 7.0)

# check that the translate operation didn't change anything
tranlateAfterEdit = translateAttr.Get()
self.assertEqual(tranlateBeforeEdit, tranlateAfterEdit)

def testTranslateLockedLayer(self):
# create new stage
cmds.file(new=True, force=True)

# Open usdCylinder.ma scene in testSamples
mayaUtils.openCylinderScene()

# get the stage
proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
proxyShapePath = proxyShapes[0]
stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

# cylinder prim
cylinderPrim = stage.GetPrimAtPath('/pCylinder1')
self.assertIsNotNone(cylinderPrim)

# create a sub-layer.
rootLayer = stage.GetRootLayer()
subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="LayerA")[0]

# check to see the if the sublayers was added
addedLayers = [subLayerA]
self.assertEqual(rootLayer.subLayerPaths, addedLayers)

# set the edit target to LayerA.
cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerA)

# set permission to edit to false
layerA = Sdf.Find(subLayerA)
layerA.SetPermissionToEdit(False)

# Check that our helper function is returning the right thing
self.assertFalse(mayaUsdUfe.isEditTargetLayerModifiable(stage))

# Get translate attribute and make sure its empty
translateAttr = cylinderPrim.GetAttribute('xformOp:translate')
self.assertIsNotNone(translateAttr)

tranlateBeforeEdit = translateAttr.Get()

# create a transform3d and translate
cylinderPath = ufe.Path([
mayaUtils.createUfePathSegment("|mayaUsdTransform|shape"),
usdUtils.createUfePathSegment("/pCylinder1")])
cylinderItem = ufe.Hierarchy.createItem(cylinderPath)

cylinderT3d = ufe.Transform3d.transform3d(cylinderItem)
if cylinderT3d is not None:
cylinderT3d.translate(5.0, 6.0, 7.0)

# check that the translate operation didn't change anything
tranlateAfterEdit = translateAttr.Get()
self.assertEqual(tranlateBeforeEdit, tranlateAfterEdit)

if __name__ == '__main__':
unittest.main(verbosity=2)