-
Notifications
You must be signed in to change notification settings - Fork 77
Fixed windows photo attachment and refactored input functions #4310
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
base: master
Are you sure you want to change the base?
Changes from all commits
1aa62c7
7b46f55
8146774
27d7566
43bbae0
21ceeb9
1380437
41336fc
052cb72
3342e3f
38a6fb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -91,9 +91,10 @@ bool InputUtils::removeFile( const QString &filePath ) | |||||||||
| bool InputUtils::copyFile( const QString &srcPath, const QString &dstPath ) | ||||||||||
| { | ||||||||||
| QString modSrcPath = srcPath; | ||||||||||
| if ( srcPath.startsWith( "file://" ) ) | ||||||||||
| QUrl url( srcPath ); | ||||||||||
| if ( url.isValid() && url.isLocalFile() ) | ||||||||||
| { | ||||||||||
| modSrcPath = modSrcPath.replace( "file://", "" ); | ||||||||||
| modSrcPath = url.toLocalFile(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| QFileInfo fi( dstPath ); | ||||||||||
|
|
@@ -994,7 +995,7 @@ QString InputUtils::resolveTargetDir( const QString &homePath, const QVariantMap | |||||||||
| if ( !expression.isEmpty() ) | ||||||||||
| { | ||||||||||
| QString result = evaluateExpression( pair, activeProject, expression ); | ||||||||||
| sanitizeFileName( result ); | ||||||||||
| sanitizePath( result ); | ||||||||||
| return result; | ||||||||||
| } | ||||||||||
| else | ||||||||||
|
|
@@ -1043,38 +1044,31 @@ QString InputUtils::resolvePath( const QString &path, const QString &homePath, c | |||||||||
|
|
||||||||||
| QString InputUtils::getRelativePath( const QString &path, const QString &prefixPath ) | ||||||||||
| { | ||||||||||
| QString modPath = path; | ||||||||||
| QString filePrefix( "file://" ); | ||||||||||
|
|
||||||||||
| if ( path.startsWith( filePrefix ) ) | ||||||||||
| // clean the file prefix | ||||||||||
| QString cleanPath = path; | ||||||||||
| const QUrl url( path ); | ||||||||||
| if ( url.isValid() && url.isLocalFile() ) | ||||||||||
| { | ||||||||||
| modPath = modPath.replace( filePrefix, QString() ); | ||||||||||
| cleanPath = url.toLocalFile(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if ( prefixPath.isEmpty() ) return modPath; | ||||||||||
|
|
||||||||||
| // Do not use a canonical path for non-existing path | ||||||||||
| if ( !QFileInfo::exists( path ) ) | ||||||||||
| // if no prefix is provided, return the cleaned absolute path | ||||||||||
| if ( prefixPath.isEmpty() ) | ||||||||||
| { | ||||||||||
| if ( !prefixPath.isEmpty() && modPath.startsWith( prefixPath ) ) | ||||||||||
| { | ||||||||||
| return modPath.replace( prefixPath, QString() ); | ||||||||||
| } | ||||||||||
| return cleanPath; | ||||||||||
| } | ||||||||||
| else | ||||||||||
| { | ||||||||||
| QDir absoluteDir( modPath ); | ||||||||||
| QDir prefixDir( prefixPath ); | ||||||||||
| QString canonicalPath = absoluteDir.canonicalPath(); | ||||||||||
| QString prefixCanonicalPath = prefixDir.canonicalPath() + "/"; | ||||||||||
|
|
||||||||||
| if ( prefixCanonicalPath.length() > 1 && canonicalPath.startsWith( prefixCanonicalPath ) ) | ||||||||||
| { | ||||||||||
| return canonicalPath.replace( prefixCanonicalPath, QString() ); | ||||||||||
| } | ||||||||||
| // use QDir to calculate the relative path | ||||||||||
| const QDir prefixDir( prefixPath ); | ||||||||||
| QString relativePath = prefixDir.relativeFilePath( cleanPath ); | ||||||||||
|
|
||||||||||
| // check if the path starts with ".." or is absolute (on Windows/different drives), it's not a "child" | ||||||||||
| if ( relativePath.startsWith( QLatin1String( ".." ) ) || QDir::isAbsolutePath( relativePath ) ) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| { | ||||||||||
| return {}; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return QString(); | ||||||||||
| return relativePath; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| void InputUtils::logMessage( const QString &message, const QString &tag, Qgis::MessageLevel level ) | ||||||||||
|
|
@@ -1964,64 +1958,106 @@ QUrl InputUtils::iconFromGeometry( const Qgis::GeometryType &geometry ) | |||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| void InputUtils::sanitizeFileName( QString &fileName ) | ||||||||||
| QString InputUtils::sanitizeNode( const QString &input ) | ||||||||||
| { | ||||||||||
| // regex captures ascii codes 0 to 31 and windows path forbidden characters <>:|?*" | ||||||||||
| const thread_local QRegularExpression illegalChars( QStringLiteral( "[\x00-\x19<>:|?*\"]" ) ); | ||||||||||
| fileName.replace( illegalChars, QStringLiteral( "_" ) ); | ||||||||||
| fileName = fileName.trimmed(); | ||||||||||
| if ( input.isEmpty() ) return input; | ||||||||||
|
|
||||||||||
| // Trim whitespace immediately before the final extension, e.g. "name .jpg" -> "name.jpg" | ||||||||||
| const int lastDot = fileName.lastIndexOf( QChar( '.' ) ); | ||||||||||
| if ( lastDot > 0 ) | ||||||||||
| // trim the whitespace at the beginning and the end | ||||||||||
| QString cleanOutput = input; | ||||||||||
| cleanOutput = cleanOutput.trimmed(); | ||||||||||
|
|
||||||||||
| // remove illegal characters before using QFileInfo | ||||||||||
| const static QRegularExpression illegalChars( QStringLiteral( "[\x00-\x1f<>:|?*\"/\\\\]" ) ); | ||||||||||
| cleanOutput.replace( illegalChars, QStringLiteral( "_" ) ); | ||||||||||
|
|
||||||||||
| // split base and suffix to trim whitespace correctly. | ||||||||||
| QFileInfo fi( cleanOutput ); | ||||||||||
| QString base = fi.completeBaseName().trimmed(); | ||||||||||
| QString suffix = fi.suffix().trimmed(); | ||||||||||
gabriel-bolbotina marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| // handle Windows Reserved Names (CON, PRN, etc.) | ||||||||||
| #ifdef Q_OS_WIN | ||||||||||
| if ( base.trimmed().length() == 3 || base.trimmed().length() == 4 ) | ||||||||||
| { | ||||||||||
| const QString base = fileName.first( lastDot ).trimmed(); | ||||||||||
| const QString ext = fileName.sliced( lastDot ); | ||||||||||
| fileName = base + ext; | ||||||||||
| const static QRegularExpression reservedNames( | ||||||||||
| QStringLiteral( "^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$" ), | ||||||||||
| QRegularExpression::CaseInsensitiveOption | ||||||||||
| ); | ||||||||||
| if ( reservedNames.match( base.trimmed() ).hasMatch() ) | ||||||||||
| { | ||||||||||
| base = base.trimmed() + QLatin1Char( '_' ); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will get changed to |
||||||||||
| } | ||||||||||
| } | ||||||||||
| #endif | ||||||||||
|
|
||||||||||
| // reassemble | ||||||||||
| if ( !suffix.isEmpty() ) | ||||||||||
| { | ||||||||||
| return base + QLatin1Char( '.' ) + suffix; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will also get changed to |
||||||||||
| } | ||||||||||
| return base; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| void InputUtils::sanitizePath( QString &path ) | ||||||||||
| { | ||||||||||
| const bool pathStartsWithFileURL = path.startsWith( "file://" ); | ||||||||||
| if ( path.isEmpty() ) return; | ||||||||||
| QString cleanPath = path; | ||||||||||
|
|
||||||||||
| // check if the path has a file prefix | ||||||||||
| // QUrl treats ? as a query start, # as a fragment | ||||||||||
| // so we modify the cleanPath for the file prefix check | ||||||||||
| cleanPath.replace( QStringLiteral( "?" ), QStringLiteral( "_" ) ); | ||||||||||
| cleanPath.replace( QStringLiteral( "#" ), QStringLiteral( "_" ) ); | ||||||||||
|
|
||||||||||
| // parse file prefix and path | ||||||||||
| QUrl url = QUrl::fromUserInput( cleanPath ); | ||||||||||
gabriel-bolbotina marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
| bool isUrl = path.startsWith( QLatin1String( "file:" ), Qt::CaseInsensitive ); | ||||||||||
gabriel-bolbotina marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+2013
to
+2014
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| if ( pathStartsWithFileURL ) | ||||||||||
| // if it has the file prefix, we will get rid of it | ||||||||||
| if ( isUrl ) | ||||||||||
| { | ||||||||||
| // remove file:// prefix before sanitization | ||||||||||
| path.remove( 0, 7 ); | ||||||||||
| cleanPath = url.toLocalFile(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| const bool pathStartsWithSlash = path.startsWith( '/' ); | ||||||||||
| // normalize separators | ||||||||||
| // convert all backslashes to forward slashes to ensure consistent splitting | ||||||||||
| cleanPath.replace( QStringLiteral( "\\" ), QStringLiteral( "/" ) ); | ||||||||||
|
|
||||||||||
| const QStringList parts = path.split( '/', Qt::SkipEmptyParts ); | ||||||||||
| QString sanitizedPath; | ||||||||||
| // split and sanitize each segment | ||||||||||
| QStringList parts = cleanPath.split( QStringLiteral( "/" ), Qt::SkipEmptyParts ); | ||||||||||
| QStringList sanitizedParts; | ||||||||||
|
|
||||||||||
| for ( int i = 0; i < parts.size(); ++i ) | ||||||||||
| { | ||||||||||
| QString part = parts.at( i ); | ||||||||||
| sanitizeFileName( part ); | ||||||||||
| const QString &part = parts[i]; | ||||||||||
|
|
||||||||||
| if ( i > 0 ) | ||||||||||
| // keep Windows Drive Letters (e.g. "C:") | ||||||||||
| if ( i == 0 && part.endsWith( QStringLiteral( ":" ) ) && part.size() == 2 ) | ||||||||||
| { | ||||||||||
| sanitizedPath += '/'; | ||||||||||
| sanitizedParts << part; | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| sanitizedPath += part; | ||||||||||
| sanitizedParts << sanitizeNode( part ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if ( pathStartsWithSlash ) | ||||||||||
| // reconstruct with Unix-style separators (/) | ||||||||||
| QString result = sanitizedParts.join( QStringLiteral( "/" ) ); | ||||||||||
|
|
||||||||||
| // handle Absolute Paths | ||||||||||
| if ( cleanPath.startsWith( QStringLiteral( "/" ) ) ) | ||||||||||
| { | ||||||||||
| // restore leading slash | ||||||||||
| sanitizedPath = '/' + sanitizedPath; | ||||||||||
| result.prepend( QStringLiteral( "/" ) ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if ( pathStartsWithFileURL ) | ||||||||||
| // restore file prefix protocol | ||||||||||
| if ( isUrl ) | ||||||||||
| { | ||||||||||
| // restore file:// prefix | ||||||||||
| sanitizedPath = "file://" + sanitizedPath; | ||||||||||
| path = QUrl::fromLocalFile( result ).toString(); | ||||||||||
| } | ||||||||||
| else | ||||||||||
| { | ||||||||||
| path = result; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| path = sanitizedPath; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| QSet<int> InputUtils::referencedAttributeIndexes( QgsVectorLayer *layer, const QString &expression ) | ||||||||||
|
|
||||||||||
gabriel-bolbotina marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -274,7 +274,7 @@ void TestUtilsFunctions::getRelativePath() | |
| QString relativePath2 = mUtils->getRelativePath( path2, prefixPath ); | ||
| QCOMPARE( fileName2, relativePath2 ); | ||
|
|
||
| QString path3 = QStringLiteral( "file://" ) + path2; | ||
| QString path3 = QUrl( path2 ).toString(); | ||
| QString relativePath3 = mUtils->getRelativePath( path3, prefixPath ); | ||
| QCOMPARE( fileName2, relativePath3 ); | ||
|
|
||
|
|
@@ -978,30 +978,6 @@ void TestUtilsFunctions::testIsValidEmail() | |
| QVERIFY( !InputUtils::isValidEmail( "brokenemail" ) ); | ||
| QVERIFY( !InputUtils::isValidEmail( "" ) ); | ||
| } | ||
|
|
||
| void TestUtilsFunctions::testSanitizeFileName() | ||
| { | ||
| // unchanged | ||
| QString str = QStringLiteral( "/simple/valid/filename.ext" ); | ||
| InputUtils::sanitizeFileName( str ); | ||
| QCOMPARE( str, QStringLiteral( "/simple/valid/filename.ext" ) ); | ||
|
|
||
| // unchanged | ||
| str = QStringLiteral( "/complex/valid/Φ!l@#äme$%^&()-_=+[]{}`~;',.ext" ); | ||
| InputUtils::sanitizeFileName( str ); | ||
| QCOMPARE( str, QStringLiteral( "/complex/valid/Φ!l@#äme$%^&()-_=+[]{}`~;',.ext" ) ); | ||
|
|
||
| // sanitized | ||
| str = QStringLiteral( "/sa ni*tized/f<i>l?n\"a:m|e .ext " ); | ||
| InputUtils::sanitizeFileName( str ); | ||
| QCOMPARE( str, QStringLiteral( "/sa ni_tized/f_i_l_n_a_m_e.ext" ) ); | ||
|
|
||
| // sanitized | ||
| str = QStringLiteral( "/sa ni*tized/.f<i>l?n\"a:m|e .co .ext " ); | ||
| InputUtils::sanitizeFileName( str ); | ||
| QCOMPARE( str, QStringLiteral( "/sa ni_tized/.f_i_l_n_a_m_e .co.ext" ) ); | ||
| } | ||
|
|
||
| void TestUtilsFunctions::testSanitizePath() | ||
| { | ||
| // unchanged | ||
|
|
@@ -1017,7 +993,7 @@ void TestUtilsFunctions::testSanitizePath() | |
| // unchanged - url prefix | ||
| str = QStringLiteral( "file://simple/valid/filename.ext" ); | ||
| InputUtils::sanitizePath( str ); | ||
| QCOMPARE( str, QStringLiteral( "file://simple/valid/filename.ext" ) ); | ||
| QCOMPARE( str, QStringLiteral( "file:///simple/valid/filename.ext" ) ); | ||
|
|
||
| // unchanged - url prefix with slash | ||
| str = QStringLiteral( "file:///simple/valid/filename.ext" ); | ||
|
|
@@ -1027,7 +1003,17 @@ void TestUtilsFunctions::testSanitizePath() | |
| // unchanged | ||
| str = QStringLiteral( "/complex/valid/Φ!l@#äme$%^&()-_=+[]{}`~;',.ext" ); | ||
| InputUtils::sanitizePath( str ); | ||
| QCOMPARE( str, QStringLiteral( "/complex/valid/Φ!l@#äme$%^&()-_=+[]{}`~;',.ext" ) ); | ||
| QCOMPARE( str, QStringLiteral( "/complex/valid/Φ!l@_äme$%^&()-_=+[]{}`~;',.ext" ) ); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please note down somewhere that |
||
|
|
||
| // unchanged with partition letter on Windows | ||
| str = QStringLiteral( "C:/Users/simple/valid/filename.ext" ); | ||
| InputUtils::sanitizePath( str ); | ||
| QCOMPARE( str, QStringLiteral( "C:/Users/simple/valid/filename.ext" ) ); | ||
gabriel-bolbotina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // unchanged with partition letter on Windows and file prefix | ||
| str = QStringLiteral( "file:///C:/Users/simple/valid/filename.ext" ); | ||
| InputUtils::sanitizePath( str ); | ||
| QCOMPARE( str, QStringLiteral( "file:///C:/Users/simple/valid/filename.ext" ) ); | ||
|
|
||
| // sanitized | ||
| str = QStringLiteral( "/sa ni*tized/f<i>l?n\"a:m|e.ext " ); | ||
|
|
@@ -1048,4 +1034,9 @@ void TestUtilsFunctions::testSanitizePath() | |
| str = QStringLiteral( "project name / project .qgz " ); | ||
| InputUtils::sanitizePath( str ); | ||
| QCOMPARE( str, QStringLiteral( "project name/project.qgz" ) ); | ||
|
|
||
| // sanitized with partition letter | ||
| str = QStringLiteral( "C:/project name / project .qgz " ); | ||
| InputUtils::sanitizePath( str ); | ||
| QCOMPARE( str, QStringLiteral( "C:/project name/project.qgz" ) ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would replace all
QLatin1Charthat you used withQStringLiteralThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think QLatin1Char is more efficient in this case because it does not create a whole Qstring object and it is used only for a character.
Are there any benefits to changing them to QStringLiteral in the modified functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading the documentation it doesn't look like it's that straight forward. This part talks mostly about what we are discussing. However I would like to highlight one thing in the beginning: