Conversation
📦 Build Artifacts Ready
|
Coverage Report for CI Build 35865501295Coverage increased (+0.2%) to 59.72%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions357 previously-covered lines in 2 files lost coverage.
Coverage Stats
💛 - Coveralls |
📦 Build Artifacts Ready
|
Withalion
left a comment
There was a problem hiding this comment.
Looks like in some cases the photo renaming doesn't work correctly. Either it get's renamed again, when using now() or the name somehow gets mixed with previous value as well. Have a look at this project Matej-test/photo-naming-expr and POI layer.
Renaming gets triggered on empty photo fields with reuse last entered value turned on.
My comment on the issue about unlinking/deleting is addressed by #4685?
Add some unit tests for using default value on photos in testattributecontroller.h
📦 Build Artifacts Ready
|
📦 Build Artifacts Ready
|
efee4cb to
99a76d6
Compare
📦 Build Artifacts Ready
|
tomasMizera
left a comment
There was a problem hiding this comment.
Looks good! Please adjust these minor comments and we can go testing. We will need extensive photo testing here @IvaKuklica
|
|
||
| if ( !fi.isFile() ) | ||
| { | ||
| ++formItemsIterator; |
There was a problem hiding this comment.
Can we add CoreUtils::log here saying something like "user wanted to reuse photo value, but the value is not a valid file"?
|
|
||
| if ( !InputUtils::copyFile( src, dst ) ) | ||
| { | ||
| ++formItemsIterator; |
There was a problem hiding this comment.
Same here, log the problem
| if ( itemData->editorWidgetType() == QStringLiteral( "ExternalResource" ) && !rememberedValue.toString().isEmpty() ) | ||
| { | ||
| const QVariantMap config = itemData->editorWidgetConfig(); | ||
| QString targetDir, prefix; | ||
| resolveExternalResourcePaths( config, targetDir, prefix ); | ||
| const QString src = InputUtils::getAbsolutePath( rememberedValue.toString(), prefix ); | ||
| const QFileInfo fi( src ); | ||
|
|
||
| if ( !fi.isFile() ) | ||
| { | ||
| ++formItemsIterator; | ||
| continue; | ||
| } | ||
|
|
||
| // temporary name; renamePhotos() applies the custom naming expression at save | ||
| QString newName = QDateTime::currentDateTime().toString( QStringLiteral( "yyyyMMdd_HHmmsszzz" ) ); | ||
| if ( !fi.suffix().isEmpty() ) | ||
| newName += QStringLiteral( "." ) + fi.suffix(); | ||
|
|
||
| const QString dst = CoreUtils::findUniquePath( InputUtils::getAbsolutePath( newName, targetDir ) ); | ||
|
|
||
| if ( !InputUtils::copyFile( src, dst ) ) | ||
| { | ||
| ++formItemsIterator; | ||
| continue; | ||
| } | ||
|
|
||
| valueToUse = InputUtils::getRelativePath( dst, prefix ); | ||
| } |
There was a problem hiding this comment.
Could we put this whole body to a dedicated method? It will be easier to read then, something like:
| if ( itemData->editorWidgetType() == QStringLiteral( "ExternalResource" ) && !rememberedValue.toString().isEmpty() ) | |
| { | |
| const QVariantMap config = itemData->editorWidgetConfig(); | |
| QString targetDir, prefix; | |
| resolveExternalResourcePaths( config, targetDir, prefix ); | |
| const QString src = InputUtils::getAbsolutePath( rememberedValue.toString(), prefix ); | |
| const QFileInfo fi( src ); | |
| if ( !fi.isFile() ) | |
| { | |
| ++formItemsIterator; | |
| continue; | |
| } | |
| // temporary name; renamePhotos() applies the custom naming expression at save | |
| QString newName = QDateTime::currentDateTime().toString( QStringLiteral( "yyyyMMdd_HHmmsszzz" ) ); | |
| if ( !fi.suffix().isEmpty() ) | |
| newName += QStringLiteral( "." ) + fi.suffix(); | |
| const QString dst = CoreUtils::findUniquePath( InputUtils::getAbsolutePath( newName, targetDir ) ); | |
| if ( !InputUtils::copyFile( src, dst ) ) | |
| { | |
| ++formItemsIterator; | |
| continue; | |
| } | |
| valueToUse = InputUtils::getRelativePath( dst, prefix ); | |
| } | |
| if ( itemData->editorWidgetType() == QStringLiteral( "ExternalResource" ) && !rememberedValue.toString().isEmpty() ) | |
| { | |
| bool res = cloneImage(params) | |
| if ( !res ) | |
| { | |
| ++formItemsIterator; | |
| continue; | |
| } | |
| } |
Description
Fixes a bug where enabling "Use last entered value" on a photo/file field causes the previous feature's photo file to be corrupted, renamed, or orphaned when reused by a new feature - most visibly when a custom filename expression is set in QGIS, where the previous feature's file gets physically renamed and reassigned to the new feature, leaving the previous feature with a broken/missing image.
Fixes: #4487
What changed
AttributeController::updateOnFeatureChange()now clones the underlying file when a photo/file field's value is reused, instead of just copying the path string. Each feature gets its own independent file from the moment the drawer opens, so it's never possible for two features to end up sharing (and therefore corrupting) the same photo.FormItem(isReusedValue(),reusedCopyPath()) so it can be cleaned up if it's never actually needed:AttributeController::discardReusedPhotoCopies()deletes it if the draft feature is discarded (cancel/rollback), or if the photo is deleted or replaced before the feature is saved.renamePhotos()now evaluates the project's custom filename expression fresh against the clone (instead of skipping reused fields entirely), so a reused photo gets a correctly computed, per-feature filename rather than a generic incremented name. A static expression that collides with an existing file still gets a proper incremental suffix.recalculateDefaultValues()no longer lets a QGIS field-level Default Value expression overwrite a value that was just populated by "reuse last entered value" while the form is open.InputUtils::resolveTargetDir()now resolves a relative root path expression/Default Path against the project home path. Previously a relative path (e.g.'photos/' + @layer_name) was used as-is, which could resolve to the wrong location on disk and produce a broken/missing image.MMActivity.importImage) and iOS (IOSGalleryPickerDelegate) gallery import now create the destination directory before copying the picked image, matching what capture-from-camera already did.Behaviour
TLDR @Withalion
"Use last entered value" on a photo field now clones the file per feature instead of sharing/renaming the original, so reused photos never corrupt or orphan another feature's image, on save or on cancel.