From 1e805bc8267896eaf0e37a4fe2dbbe10b35d25e8 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Mon, 14 Oct 2024 22:15:54 -0400 Subject: [PATCH 01/15] Updated documentation about how to override property `selection.multi.separator` to change the default behavior. #108 --- UserManual.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/UserManual.md b/UserManual.md index 68b1ff4..dbcd29f 100644 --- a/UserManual.md +++ b/UserManual.md @@ -1720,9 +1720,21 @@ The system will generates the following property values (note the `\r\n` charact If you need more flexibility when dealing with multiple files, the system defines the property `selection.multi.separator` that allows customizing the separator when combining multiple files. -By default, this property is set to the value `\r\n` (new line) when the application initialize. +By default, this property is set to the value `\r\n` (new line) when the application initialize. -The property can be modified at any time using a [<property>](#property-action) action for changing the property when a menu is executed or with the [<default>](#default) element to change the value globally (when the `Configuration File` is loaded). +The property can be modified at any time using a [<property>](#property-action) action or with the [<default>](#default) element to change it globally. + +If you mostly need the opposite of the default behavior (double-quotes instead of CRLF), override the value of the property globally when the `Configuration File` is loaded : +```xml + + + + + + + + +``` To reset the property back to the default value, use the following <property> action: ```xml @@ -1731,7 +1743,7 @@ To reset the property back to the default value, use the following <property& **Example #1:** -If an executable must get the list of selected files in a single command line (one after the other), one can set the property `selection.multi.separator` to `" "` (double quote, space, double quote) and use the string `"${selection.path}"` (including the double quotes) to get the required expanded value: +If an executable must get the list of selected files in a single command line (one after the other), one can temporary set the property `selection.multi.separator` to `" "` (double quote, space, double quote) and use the string `"${selection.path}"` (including the double quotes) to get the required expanded value: ```xml @@ -1740,13 +1752,15 @@ If an executable must get the list of selected files in a single command line (o ``` + Which result in the following expanded value: ``` "C:\Program Files (x86)\Winamp\libFLAC.dll" "C:\Program Files (x86)\Winamp\winamp.exe" "C:\Program Files (x86)\Winamp\zlib.dll" ``` + **Example #2:** -Assume that you want to run a specific command on each of the selected files (for example reset the files attributes), one can set the property `selection.multi.separator` to `${line.separator}attrib -r -a -s -h "` (including the ending double quote character) and use the string `attrib -r -a -s -h "${selection.filename}"` (including the double quotes) to get the following expanded value: +Assume that you want to run a specific command on each of the selected files (for example reset the files attributes), one can set the property `selection.multi.separator` to `${line.separator}attrib -r -a -s -h "` (including the ending double quote character) and use the string `attrib -r -a -s -h "${selection.filename}"` (including the double quotes) to get the following expanded value: ``` attrib -r -a -s -h "C:\Program Files (x86)\Winamp\libFLAC.dll" attrib -r -a -s -h "C:\Program Files (x86)\Winamp\winamp.exe" From e074bc90b0efbb40053120bee1ed2f9a93659325 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Fri, 25 Oct 2024 09:58:21 -0400 Subject: [PATCH 02/15] Updated documentation about reordering registry keys to reorder Windows Context Menu elements. #108 --- UserManual.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/UserManual.md b/UserManual.md index dbcd29f..c33cb8c 100644 --- a/UserManual.md +++ b/UserManual.md @@ -79,6 +79,7 @@ This manual includes a description of the system functionalities and capabilitie * [Troubleshooting](#troubleshooting) * [Logging support](#logging-support) * [Checkout the _Tools_ section](#tools) + * [Change the rendering order of your system's shell extension menus](#change-the-rendering-order-of-your-systems-shell-extension-menus) * [Missing ampersand character (`&`) in menus](#missing-ampersand-character--in-menus) * [Reporting bugs](#reporting-bugs) @@ -2729,6 +2730,33 @@ If no option is specified, verbose mode is disabled. +## Change the rendering order of your system's shell extension menus ## + +ShellAnything does not control in which order the system renders all the registered Shell Extensions. Because of this, your ShellAnything menus could be rendered at the top, middle or the end of Window's Context menu. + +This behavior can however be altered. + +**Note:** +Changing the default order of ShellAnything's menu is not officially supported by ShellAnything. The feature is documented as working on Windows 10 (22H2 or later) and Windows 11. This is basically a hack. The method is added to ShellAnything's documentation because some users have a need for it. + +Windows renders the registered Shell Extension alphabetically. For example, to change the rendering order of Shell Extensions for directories, you need to rename the desired shell extension's registry key in `HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers`. + +For example: you could rename the registry key `HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers\ShellAnything` to change ShellAnything's menu order in a Windows context menu. You can rename the key to `!ShellAnything` to have ShellAnything's menus rendered to the top of the context menu. Or you can rename the registry key to `ΩShellAnything` to move ShellAnything's menus to the bottom of the context menu. + +This feature is not officially supported. If you unregister or uninstall ShellAnything, the renamed registry key will not be deleted from your system. If you re-register ShellAnything then two registry keys referencing ShellAnything will be registered on the system (`!ShellAnything` and `ShellAnything`). This could lead to adding the menus twice to the context menu. + +Another downside is that you need to do this for every locations in the registry where ShellAnything is registered as a shell extension: + +* `*` (star character) +* `Directory` +* `Directory\Background` +* `Folder` +* `Drive` + +There is also shell extensions registered under `AllFilesystemObjects` (_HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AllFilesystemObjects\shellex\ContextMenuHandlers_). ShellAnything do not register itself to this file system element. This could create confusion on the rendering order of context menus since `Directory` may (or may not) have priority over `AllFilesystemObjects`. + + + ## Missing ampersand character (`&`) in menus ## One might be puzzled as to why his menus do not display ampersand character (`&`) properly. From e6505ce65c2d4f9d1a5a4e107fc78b1bb77121b9 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Fri, 25 Oct 2024 21:54:28 -0400 Subject: [PATCH 03/15] Moved configurations under `%USERPROFILE%\ShellAnything\configs` directory. --- src/core/App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/App.cpp b/src/core/App.cpp index 25621e5..d8aea1c 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -219,7 +219,7 @@ namespace shellanything { //get home directory of the user std::string home_dir = ra::user::GetHomeDirectoryUtf8(); - std::string config_dir = home_dir + "\\" + app_name; + std::string config_dir = home_dir + "\\" + app_name + "\\configs"; return config_dir; } From 0a162f6fbf66a55551586d7522905bbbf3535c5f Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Fri, 25 Oct 2024 22:00:46 -0400 Subject: [PATCH 04/15] Created function `IsValidConfigDirectory()` which handle creating the directory if missing. Fixed possible issues with non-ansi characters in user's home directory or sub-directories. Modified multiple "utf8" variant of existing functions to prevent issues. Renamed `Logs` directory to `logs` to also match `configs` (with lower case first letter). --- src/core/App.cpp | 67 ++++++++++++++++++++++++++++++------------ src/core/App.h | 12 ++++++-- src/shared/SaUtils.cpp | 66 ++++++++++++++++++++++++++++++++++++++++- src/shared/SaUtils.h | 18 ++++++++++++ 4 files changed, 141 insertions(+), 22 deletions(-) diff --git a/src/core/App.cpp b/src/core/App.cpp index d8aea1c..7681e55 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -27,12 +27,10 @@ #include "ConfigManager.h" #include "PropertyManager.h" -#include "rapidassist/process.h" -#include "rapidassist/user.h" -#include "rapidassist/environment.h" -#include "rapidassist/filesystem.h" -#include "rapidassist/filesystem_utf8.h" +#include "rapidassist/process_utf8.h" #include "rapidassist/user_utf8.h" +#include "rapidassist/environment_utf8.h" +#include "rapidassist/filesystem_utf8.h" #include "rapidassist/unicode.h" #include "shellanything/version.h" @@ -159,7 +157,7 @@ namespace shellanything bool App::IsTestingEnvironment() { - std::string process_path = ra::process::GetCurrentProcessPath(); + std::string process_path = ra::process::GetCurrentProcessPathUtf8(); if (process_path.find("sa.tests") != std::string::npos) return true; return false; @@ -174,7 +172,7 @@ namespace shellanything //Create 'test_logs' directory under the current executable. //When running tests from a developer environment, the 'test_logs' directory is expected to have write access. - std::string log_dir = ra::process::GetCurrentProcessDir(); + std::string log_dir = ra::process::GetCurrentProcessDirUtf8(); if (!log_dir.empty()) { log_dir.append("\\test_logs"); @@ -185,7 +183,7 @@ namespace shellanything //Issue #60 - Unit tests cannot execute from installation directory. //If unit tests are executed from the installation directory, //the 'test_logs' directory under the current executable is denied write access. - log_dir = ra::environment::GetEnvironmentVariable("TEMP"); + log_dir = ra::environment::GetEnvironmentVariableUtf8("TEMP"); if (!log_dir.empty()) { log_dir.append("\\test_logs"); @@ -194,24 +192,23 @@ namespace shellanything } } - //This DLL is executed by the shell (File Explorer). + //This DLL is most probably executed by the shell (File Explorer). //By default, GLOG will output log files in %TEMP% directory. - //However, I prefer to use %USERPROFILE%\ShellAnything\Logs - - std::string log_dir = ra::user::GetHomeDirectory(); + //However, I prefer to use %USERPROFILE%\ShellAnything\logs + std::string log_dir = ra::user::GetHomeDirectoryUtf8(); if (!log_dir.empty()) { //We got the %USERPROFILE% directory. //Now add our custom path to it - log_dir.append("\\ShellAnything\\Logs"); + log_dir.append("\\" + app_name + "\\logs"); if (IsValidLogDirectory(log_dir)) return log_dir; } //Failed getting HOME directory. //Fallback to using %TEMP%. - log_dir = ra::environment::GetEnvironmentVariable("TEMP"); + log_dir = ra::environment::GetEnvironmentVariableUtf8("TEMP"); return log_dir; } @@ -219,7 +216,18 @@ namespace shellanything { //get home directory of the user std::string home_dir = ra::user::GetHomeDirectoryUtf8(); - std::string config_dir = home_dir + "\\" + app_name + "\\configs"; + std::string app_dir = home_dir + "\\" + app_name; + std::string config_dir; + + if (!app_dir.empty()) + { + //We got the %USERPROFILE% directory. + //Now add our custom path to it + config_dir = app_dir + +"\\configs"; + if (IsValidConfigDirectory(config_dir)) + return config_dir; + } + return config_dir; } @@ -237,16 +245,39 @@ namespace shellanything //Issue #60 - Unit tests cannot execute from installation directory. //Check if the directory already exists - if (!ra::filesystem::DirectoryExists(path.c_str())) + if (!ra::filesystem::DirectoryExistsUtf8(path.c_str())) { //Try to create the directory. - bool created = ra::filesystem::CreateDirectory(path.c_str()); + bool created = ra::filesystem::CreateDirectoryUtf8(path.c_str()); if (!created) return false; } //Validate that directory path is writable. - bool write_access = HasDirectoryWriteAccess(path); + bool write_access = HasDirectoryWriteAccessUtf8(path); + if (!write_access) + return false; //Write to directory is denied. + + //Write to directory is granted. + return true; + } + + bool App::IsValidConfigDirectory(const std::string& path) + { + // Config directory must be accessible for reading. + // Write access is optional + + //Check if the directory already exists + if (!ra::filesystem::DirectoryExistsUtf8(path.c_str())) + { + //Try to create the directory. + bool created = ra::filesystem::CreateDirectoryUtf8(path.c_str()); + if (!created) + return false; + } + + //Validate that directory path is readable. + bool write_access = HasDirectoryReadAccessUtf8(path); if (!write_access) return false; //Write to directory is denied. diff --git a/src/core/App.h b/src/core/App.h index 1873167..1aa31b7 100644 --- a/src/core/App.h +++ b/src/core/App.h @@ -188,6 +188,12 @@ namespace shellanything /// Returns the path of the directory that should be used by the logging framework. std::string GetLogDirectory(); + /// + /// Test if the given directory is valid for logging. + /// + /// Returns true if the directory is valid for logging. Returns false otherwise. + bool IsValidLogDirectory(const std::string& path); + /// /// Get the application's configurations directory. /// @@ -195,10 +201,10 @@ namespace shellanything std::string GetConfigurationsDirectory(); /// - /// Test if the given directory is valid for logging. + /// Test if the given directory is valid for reading configurations. /// - /// Returns true if the directory is valid for logging. Returns false otherwise. - bool IsValidLogDirectory(const std::string& path); + /// Returns true if the directory is valid for reading configurations. Returns false otherwise. + bool IsValidConfigDirectory(const std::string& path); /// /// Initialize and start the application ready for usage. diff --git a/src/shared/SaUtils.cpp b/src/shared/SaUtils.cpp index 3e1478e..1d4e88f 100644 --- a/src/shared/SaUtils.cpp +++ b/src/shared/SaUtils.cpp @@ -27,7 +27,7 @@ #include "rapidassist/errors.h" #include "rapidassist/unicode.h" #include "rapidassist/strings.h" -#include "rapidassist/filesystem.h" +#include "rapidassist/filesystem_utf8.h" #define WIN32_LEAN_AND_MEAN 1 #include //for MAX_PATH @@ -114,6 +114,44 @@ std::string GetCurrentModulePathUtf8() return path; } +//Test if a directory allow read access to the current user. +//Note: the only way to detect if read access is available is to actually try to list files in the directory +bool HasDirectoryReadAccess(const std::string& path) +{ + //Check if the directory already exists + if (!ra::filesystem::DirectoryExists(path.c_str())) + return false; //Directory not found. Denied read access. + + ra::strings::StringVector files; + bool success = ra::filesystem::FindFiles(files, path.c_str(), 0); + if (!success) + return false; // Unable to list files in the given directory + + if (files.empty()) + return true; // Assume directory is empty + + return true; +} + +//Test if a directory allow read access to the current user. +//Note: the only way to detect if read access is available is to actually try to list files in the directory +bool HasDirectoryReadAccessUtf8(const std::string& path) +{ + //Check if the directory already exists + if (!ra::filesystem::DirectoryExistsUtf8(path.c_str())) + return false; //Directory not found. Denied read access. + + ra::strings::StringVector files; + bool success = ra::filesystem::FindFilesUtf8(files, path.c_str(), 0); + if (!success) + return false; // Unable to list files in the given directory + + if (files.empty()) + return true; // Assume directory is empty + + return true; +} + //Test if a directory allow write access to the current user. //Note: the only way to detect if write access is available is to actually write a file bool HasDirectoryWriteAccess(const std::string& path) @@ -140,6 +178,32 @@ bool HasDirectoryWriteAccess(const std::string& path) return true; } +//Test if a directory allow write access to the current user. +//Note: the only way to detect if write access is available is to actually write a file +bool HasDirectoryWriteAccessUtf8(const std::string& path) +{ + //Check if the directory already exists + if (!ra::filesystem::DirectoryExistsUtf8(path.c_str())) + return false; //Directory not found. Denied write access. + + //Generate a random filename to use as a "temporary file". + std::string filename = ra::filesystem::GetTemporaryFileName(); + + //Try to create a file. This will validate that we have write access to the directory. + std::string file_path = path + ra::filesystem::GetPathSeparatorStr() + filename; + static const std::string data = __FUNCTION__; + bool file_created = ra::filesystem::WriteFileUtf8(file_path, data); + if (!file_created) + return false; //Write is denied + + //Write is granted + + //Cleaning up + bool deleted = ra::filesystem::DeleteFileUtf8(file_path.c_str()); + + return true; +} + bool IsFirstApplicationRun(const std::string& name, const std::string& version) { std::string key = ra::strings::Format("HKEY_CURRENT_USER\\Software\\%s\\%s", name.c_str(), version.c_str()); diff --git a/src/shared/SaUtils.h b/src/shared/SaUtils.h index 1603ae2..5342c57 100644 --- a/src/shared/SaUtils.h +++ b/src/shared/SaUtils.h @@ -39,12 +39,30 @@ std::string GetCurrentModulePath(); /// Returns the path of the current DLL if successful. Returns an empty string on error. std::string GetCurrentModulePathUtf8(); +/// +/// Test if a directory has read access. +/// +/// Returns true if read access is granted. Returns false otherwise. +bool HasDirectoryReadAccess(const std::string& path); + +/// +/// Test if a directory has read access. +/// +/// Returns true if read access is granted. Returns false otherwise. +bool HasDirectoryReadAccessUtf8(const std::string& path); + /// /// Test if a directory has write access. /// /// Returns true if write access is granted. Returns false otherwise. bool HasDirectoryWriteAccess(const std::string& path); +/// +/// Test if a directory has write access. +/// +/// Returns true if write access is granted. Returns false otherwise. +bool HasDirectoryWriteAccessUtf8(const std::string& path); + /// /// Returns true if the application is run for the first time. /// Note, for Windows users, the implementation is based on registry keys in HKEY_CURRENT_USER\Software\name\version. From 45dcfd214d17668c563d773cb8fc52372a039a32 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Fri, 25 Oct 2024 22:12:15 -0400 Subject: [PATCH 05/15] Removed all configuration files from in the `configurations` directory from being inserted in `sa.core` project. The only remaining configuration files are `default.xml` and `shellanything.xml`. --- src/CMakeLists.txt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3489e14..54fd1e4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,18 +16,7 @@ configure_file( ${CMAKE_SOURCE_DIR}/src/version.rc.in ${SHELLANYTHING_VERSION_RC set(CONFIGURATION_DEFAULT_FILES "" ${CMAKE_SOURCE_DIR}/resources/configurations/default.xml - "${CMAKE_SOURCE_DIR}/resources/configurations/git.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/Hugo.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/Mark Text.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/" - "${CMAKE_SOURCE_DIR}/resources/configurations/" - "${CMAKE_SOURCE_DIR}/resources/configurations/Microsoft Office 2003.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/Microsoft Office 2007.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/Microsoft Office 2010.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/Microsoft Office 2013.xml" - "${CMAKE_SOURCE_DIR}/resources/configurations/Microsoft Office 2016.xml" ${CMAKE_SOURCE_DIR}/resources/configurations/shellanything.xml - ${CMAKE_SOURCE_DIR}/resources/configurations/WinDirStat.xml ) # Subprojects From fb6394e03cdb8146c3539a68fba96c921fa3f479 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Fri, 25 Oct 2024 22:15:57 -0400 Subject: [PATCH 06/15] * Renamed configurations directory from `configs` to `configurations` to match the source code directory `ShellAnything/resources/configurations`. --- src/core/App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/App.cpp b/src/core/App.cpp index 7681e55..7bba43d 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -223,7 +223,7 @@ namespace shellanything { //We got the %USERPROFILE% directory. //Now add our custom path to it - config_dir = app_dir + +"\\configs"; + config_dir = app_dir + +"\\configurations"; if (IsValidConfigDirectory(config_dir)) return config_dir; } From a596515424afcbe0a526562fbc4f0b462c8ed18d Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Fri, 25 Oct 2024 22:47:27 -0400 Subject: [PATCH 07/15] Only installing `defaults.xml` and `shellanything.xml` default configuration files. Skip any other configuration files available in the installation directory. --- src/core/App.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/core/App.cpp b/src/core/App.cpp index 7bba43d..7869dac 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -292,11 +292,6 @@ namespace shellanything static const char* default_files[] = { "default.xml", - "Microsoft Office 2003.xml", - "Microsoft Office 2007.xml", - "Microsoft Office 2010.xml", - "Microsoft Office 2013.xml", - "Microsoft Office 2016.xml", "shellanything.xml", }; static const size_t num_files = sizeof(default_files) / sizeof(default_files[0]); From 8848eb9f4c35a3008861ae088412b48f0cbd28b4 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sat, 26 Oct 2024 12:04:17 -0400 Subject: [PATCH 08/15] Implemented `RenameFile()` and `RenameFileUtf8()` with tests. --- src/shared/SaUtils.cpp | 25 +++++++++++++++++++++++++ src/shared/SaUtils.h | 14 ++++++++++++++ src/tests/TestSaUtils.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/shared/SaUtils.cpp b/src/shared/SaUtils.cpp index 1d4e88f..d9f09c0 100644 --- a/src/shared/SaUtils.cpp +++ b/src/shared/SaUtils.cpp @@ -204,6 +204,31 @@ bool HasDirectoryWriteAccessUtf8(const std::string& path) return true; } +bool RenameFile(const std::string& old_path, const std::string& new_path) +{ + if (std::rename(old_path.c_str(), new_path.c_str()) < 0) + { + return false; + } + return true; +} + +bool RenameFileUtf8(const std::string& old_path, const std::string& new_path) +{ +#ifndef WIN32 + // Win32 API not available, proceed with a normal ansi rename + return RenameFile(old_path, new_path); +#else + std::wstring old_path_w = ra::unicode::Utf8ToUnicode(old_path); + std::wstring new_path_w = ra::unicode::Utf8ToUnicode(new_path); + if (_wrename(old_path_w.c_str(), new_path_w.c_str()) < 0) + { + return false; + } + return true; +#endif +} + bool IsFirstApplicationRun(const std::string& name, const std::string& version) { std::string key = ra::strings::Format("HKEY_CURRENT_USER\\Software\\%s\\%s", name.c_str(), version.c_str()); diff --git a/src/shared/SaUtils.h b/src/shared/SaUtils.h index 5342c57..ff35e85 100644 --- a/src/shared/SaUtils.h +++ b/src/shared/SaUtils.h @@ -63,6 +63,20 @@ bool HasDirectoryWriteAccess(const std::string& path); /// Returns true if write access is granted. Returns false otherwise. bool HasDirectoryWriteAccessUtf8(const std::string& path); +/// +/// Rename a file name to another name. +/// Paths must be specified in as absolute path. If the source directory and the target directories are not the same, the file will be also moved. +/// +/// Returns true if the operation is successful. Returns false otherwise. +bool RenameFile(const std::string& old_path, const std::string& new_path); + +/// +/// Rename a file name to another name. +/// Paths must be specified in as absolute path. If the source directory and the target directories are not the same, the file will be also moved. +/// +/// Returns true if the operation is successful. Returns false otherwise. +bool RenameFileUtf8(const std::string& old_path, const std::string& new_path); + /// /// Returns true if the application is run for the first time. /// Note, for Windows users, the implementation is based on registry keys in HKEY_CURRENT_USER\Software\name\version. diff --git a/src/tests/TestSaUtils.cpp b/src/tests/TestSaUtils.cpp index 1c634d0..6035d1d 100644 --- a/src/tests/TestSaUtils.cpp +++ b/src/tests/TestSaUtils.cpp @@ -29,6 +29,10 @@ #include "TestSaUtils.h" #include "SaUtils.h" +#include "rapidassist/testing_utf8.h" +#include "rapidassist/environment_utf8.h" +#include "rapidassist/random.h" +#include "rapidassist/filesystem_utf8.h" namespace shellanything { @@ -64,6 +68,33 @@ namespace shellanything ASSERT_TRUE(IsPrintableUtf8("Espa" "\xc3" "\xb1" "ol")); // Español } //-------------------------------------------------------------------------------------------------- + TEST_F(TestSaUtils, testRenameFileUtf8) + { + static const std::string filename_characters = "abcdefghijklmnopqrstuvwxyz0123456789_"; + std::string temp_dir = ra::environment::GetEnvironmentVariableUtf8("TEMP"); + std::string old_filename = ra::random::GetRandomString(16, filename_characters.c_str()) + ".tmp"; + std::string new_filename = ra::random::GetRandomString(16, filename_characters.c_str()) + ".tmp"; + + std::string old_path = temp_dir + "\\" + old_filename; + std::string new_path = temp_dir + "\\" + new_filename; + + // assert pre-state + ASSERT_TRUE(ra::testing::CreateFileUtf8(old_path.c_str(), 10240)) << "Failed to create file: " << old_path; + ASSERT_FALSE(ra::filesystem::FileExistsUtf8(new_path.c_str())); + + // assert operation is successful + bool renamed = RenameFileUtf8(old_path, new_path); + ASSERT_TRUE(renamed); + + // assert files are actualy renamed + ASSERT_FALSE(ra::filesystem::FileExistsUtf8(old_path.c_str())); + ASSERT_TRUE (ra::filesystem::FileExistsUtf8(new_path.c_str())); + + // cleanup + ra::filesystem::DeleteFileUtf8(old_path.c_str()); + ra::filesystem::DeleteFileUtf8(new_path.c_str()); + } + //-------------------------------------------------------------------------------------------------- } //namespace test } //namespace shellanything From 1388ba1a9f035cc8cc74fcea159c0729552ce758 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sat, 26 Oct 2024 13:03:56 -0400 Subject: [PATCH 09/15] Changed Configuration Files directory to `C:\Users\%USERNAME%\ShellAnything\configurations`. --- CHANGES | 4 +++- UserManual.md | 2 +- src/core/App.cpp | 59 ++++++++++++++++++++++++++++++++++++++++-------- src/core/App.h | 14 ++++++++++++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 1b18202..e168403 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,11 @@ Changes for 0.10.0 +* **Breaking change:** The _Configuration Files_ directory has moved from `%USERPROFILE%\ShellAnything` to `%USERPROFILE%\ShellAnything\configurations`. On first application launch, Configuration Files in the old directory will move to the new directory automatically. Other files in `%USERPROFILE%\ShellAnything` will not be moved. * ShellAnything has a new high-resolution logo icon! * Shellanything now features verbose logging mode and command line arguments debugging tools. -* ShellAnything now packages icons from [icons8/flat-color-icons](https://github.com/icons8/flat-color-icons) +* ShellAnything now packages icons from [icons8/flat-color-icons](https://github.com/icons8/flat-color-icons). +Fixes: * Fixed issue #6 : (twice) Right-click on a directory with Windows Explorer in the left panel shows the menus twice. * Fixed issue #31 : (twice) Error in logs for CContextMenu::GetCommandString() * Fixed issue #109: Implement default and verbose logging. diff --git a/UserManual.md b/UserManual.md index c33cb8c..93575b3 100644 --- a/UserManual.md +++ b/UserManual.md @@ -169,7 +169,7 @@ A *configuration file* contains the definition of all [<menu>](#Menu) elem When a user right-click on a file in *Windows Explorer*, the application will load all available *configuration files* and display their content into the displayed context menu. -The list of *Configuration Files* is unique for each users of the system. The files are stored in `C:\Users\%USERNAME%\ShellAnything` directory where `%USERNAME%` is your current Windows session *username*. Note that *Windows Explorer* also support copy & pasting `C:\Users\%USERNAME%\ShellAnything` into an *address bar* to quickly jump to the directory. +The list of *Configuration Files* is unique for each users of the system. The files are stored in `C:\Users\%USERNAME%\ShellAnything\configurations` directory where `%USERNAME%` is your current Windows session *username*. Note that *Windows Explorer* also support copy & pasting `C:\Users\%USERNAME%\ShellAnything\configurations` into an *address bar* to quickly jump to the directory. The application support multiple *configuration files* at the same time. One can add new files in the *configuration directory* and the system will automatically detect and load them. diff --git a/src/core/App.cpp b/src/core/App.cpp index 7869dac..5509f28 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -212,21 +212,23 @@ namespace shellanything return log_dir; } + std::string App::GetLegacyConfigurationsDirectory() + { + //get home directory of the user + std::string home_dir = ra::user::GetHomeDirectoryUtf8(); + std::string legacy_dir = home_dir + "\\" + app_name; + return legacy_dir; + } + std::string App::GetConfigurationsDirectory() { //get home directory of the user std::string home_dir = ra::user::GetHomeDirectoryUtf8(); std::string app_dir = home_dir + "\\" + app_name; - std::string config_dir; + std::string config_dir = app_dir + +"\\configurations"; - if (!app_dir.empty()) - { - //We got the %USERPROFILE% directory. - //Now add our custom path to it - config_dir = app_dir + +"\\configurations"; - if (IsValidConfigDirectory(config_dir)) - return config_dir; - } + if (IsValidConfigDirectory(config_dir)) + return config_dir; return config_dir; } @@ -314,16 +316,55 @@ namespace shellanything } } + void App::ClearLegacyConfigurationDirectory(const std::string& legacy_dir) + { + const std::string config_dir = GetConfigurationsDirectory(); + if (legacy_dir == config_dir) + return; // nothing to do + + // Search for xml files directly under legacy_dir + ra::strings::StringVector files; + static const int depth = 0; // Do not search recursively + bool success = ra::filesystem::FindFilesUtf8(files, legacy_dir.c_str(), depth); + if (!success) + return; // aborted + + // for each file found + for (size_t i = 0; i < files.size(); i++) + { + const std::string& file_path = files[i]; + + // Is that a configuration file ? + if (ConfigFile::IsValidConfigFile(file_path)) + { + // It does not belongs there. Move it to the new configuration directory. + + std::string file_name = ra::filesystem::GetFilename(file_path.c_str()); + std::string old_path = file_path; + std::string new_path = config_dir + "\\" + file_name; + + SA_LOG(INFO) << "Moving legacy configuration file '" << old_path << "' to '" << new_path << "'."; + bool moved = RenameFileUtf8(old_path, new_path); + if (!moved) + { + SA_LOG(ERROR) << "Failed moving configuration file '" << old_path << "' to target file '" << new_path << "'."; + } + } + } + } + void App::InitConfigManager() { shellanything::ConfigManager& cmgr = shellanything::ConfigManager::GetInstance(); + std::string legacy_dir = GetLegacyConfigurationsDirectory(); std::string config_dir = GetConfigurationsDirectory(); bool first_run = IsFirstApplicationRun(app_name, app_version); if (first_run) { SA_LOG(INFO) << "First application launch."; + ClearLegacyConfigurationDirectory(legacy_dir); // Issue #108 moved Configuration Files directory to a new location. InstallDefaultConfigurations(config_dir); } diff --git a/src/core/App.h b/src/core/App.h index 1aa31b7..76e1be2 100644 --- a/src/core/App.h +++ b/src/core/App.h @@ -194,6 +194,15 @@ namespace shellanything /// Returns true if the directory is valid for logging. Returns false otherwise. bool IsValidLogDirectory(const std::string& path); + /// + /// Get the application's configurations legacy directory. + /// + /// + /// Directory was change in issue #108. + /// + /// Returns the path of the legacy directory of the user's Configuration Files. + std::string GetLegacyConfigurationsDirectory(); + /// /// Get the application's configurations directory. /// @@ -219,6 +228,11 @@ namespace shellanything /// The destination directory. void InstallDefaultConfigurations(const std::string& dest_dir); + /// + /// Moved any Configuration Files from the given legacy directory to the official configurations directory. + /// + void ClearLegacyConfigurationDirectory(const std::string& legacy_dir); + /// /// Initialize the Configuration Manager to the user's stall the original configuration files to the specified destination directory. /// From 4e8755b0ac7c5421d1e5636e260fc8e673372894 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sat, 26 Oct 2024 14:11:19 -0400 Subject: [PATCH 10/15] Renamed `ShellAnything.xcf` (source file of ShellAnything-splashscreen.jpg) to `ShellAnything-splashscreen.xcf` to better show their relation. --- ...lAnything.xcf => ShellAnything-splashscreen.xcf} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{ShellAnything.xcf => ShellAnything-splashscreen.xcf} (100%) diff --git a/docs/ShellAnything.xcf b/docs/ShellAnything-splashscreen.xcf similarity index 100% rename from docs/ShellAnything.xcf rename to docs/ShellAnything-splashscreen.xcf From 120ed4fd4a2d0031be8cbfbce7e7b555a797908e Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sat, 26 Oct 2024 15:14:27 -0400 Subject: [PATCH 11/15] Mirrored some files from `[SOURCE_CODE_DIR]\resources\icons` to map directly to `[INSTALL_DIR]\resources\icons` --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8748e85..59bf5f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,6 +379,11 @@ install(FILES ${CMAKE_SOURCE_DIR}/resources/register.bat # Resources install(DIRECTORY "${CMAKE_SOURCE_DIR}/resources/Windows Icon Tables" DESTINATION ${SHELLANYTHING_INSTALL_RESOURCE_DIR}) +install(FILES ${CMAKE_SOURCE_DIR}/resources/icons/shellanything.ico + ${CMAKE_SOURCE_DIR}/resources/icons/0.1.0.ico + ${CMAKE_SOURCE_DIR}/resources/icons/0.2.0.ico + ${CMAKE_SOURCE_DIR}/resources/icons/0.2.0-16x16-legacy.ico + DESTINATION ${SHELLANYTHING_INSTALL_RESOURCE_DIR}/icons) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/shellanything DESTINATION ${SHELLANYTHING_INSTALL_INCLUDE_DIR}) install(FILES ${SHELLANYTHING_EXPORT_HEADER} @@ -390,7 +395,6 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/licenses DESTINATION ${SHELLANYTHING_INSTA install(FILES ${CMAKE_SOURCE_DIR}/docs/screenshot_file.png ${CMAKE_SOURCE_DIR}/docs/screenshot_folder.png ${CMAKE_SOURCE_DIR}/docs/ShellAnything-splashscreen.jpg - ${CMAKE_SOURCE_DIR}/resources/icons/shellanything.ico ${CMAKE_CURRENT_BINARY_DIR}/sa_plugin_demo.zip DESTINATION ${SHELLANYTHING_INSTALL_DOC_DIR}) From ea455e093fdba7b3382a680af142d107e0cb7e35 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sat, 26 Oct 2024 15:27:11 -0400 Subject: [PATCH 12/15] Moved installer based resources from `[SOURCE_CODE_DIR]\resources` to map directly to `[SOURCE_CODE_DIR]\resources\installer`. --- CMakeLists.txt | 6 +++--- resources/{ => installer/bin}/register.bat | 0 resources/{ => installer/bin}/unregister.bat | 0 resources/{ => installer}/wix_license_template.rtf | Bin src/core/CMakeLists.txt | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) rename resources/{ => installer/bin}/register.bat (100%) rename resources/{ => installer/bin}/unregister.bat (100%) rename resources/{ => installer}/wix_license_template.rtf (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59bf5f6..d6ee691 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,8 +373,8 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/shellanything-config-version.cma configure_file(${CMAKE_SOURCE_DIR}/cmake/shellanything-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/shellanything-config.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/shellanything-config.cmake DESTINATION ${SHELLANYTHING_INSTALL_CMAKE_DIR}) -install(FILES ${CMAKE_SOURCE_DIR}/resources/register.bat - ${CMAKE_SOURCE_DIR}/resources/unregister.bat +install(FILES ${CMAKE_SOURCE_DIR}/resources/installer/bin/register.bat + ${CMAKE_SOURCE_DIR}/resources/installer/bin/unregister.bat DESTINATION ${SHELLANYTHING_INSTALL_BIN_DIR}) # Resources @@ -473,7 +473,7 @@ set(CPACK_PACKAGE_EXECUTABLES # This is intended behavior for ShellAnything. set(CPACK_WIX_UPGRADE_GUID "EBEF1698-CA72-480A-9FE3-E8A08E7E03ED") # as per v0.7.0 and older -set(CPACK_WIX_LICENSE_RTF "${CMAKE_SOURCE_DIR}/resources/wix_license_template.rtf") +set(CPACK_WIX_LICENSE_RTF "${CMAKE_SOURCE_DIR}/resources/installer/wix_license_template.rtf") set(CPACK_WIX_PRODUCT_ICON "${CMAKE_SOURCE_DIR}/resources/icons/shellanything.ico") set(CPACK_WIX_HELP_LINK "https://github.com/end2endzone/ShellAnything") set(CPACK_WIX_PROGRAM_MENU_FOLDER "${PROJECT_NAME}") diff --git a/resources/register.bat b/resources/installer/bin/register.bat similarity index 100% rename from resources/register.bat rename to resources/installer/bin/register.bat diff --git a/resources/unregister.bat b/resources/installer/bin/unregister.bat similarity index 100% rename from resources/unregister.bat rename to resources/installer/bin/unregister.bat diff --git a/resources/wix_license_template.rtf b/resources/installer/wix_license_template.rtf similarity index 100% rename from resources/wix_license_template.rtf rename to resources/installer/wix_license_template.rtf diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2f7e6a3..9f056c3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -120,11 +120,11 @@ add_custom_command( TARGET sa.core POST_BUILD # Copy registration and unregistration scripts to target dir add_custom_command( TARGET sa.core POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_SOURCE_DIR}/resources/register.bat $/register.bat + ${CMAKE_SOURCE_DIR}/resources/installer/bin/register.bat $/register.bat COMMENT "Copying register.bat script.") add_custom_command( TARGET sa.core POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_SOURCE_DIR}/resources/unregister.bat $/unregister.bat + ${CMAKE_SOURCE_DIR}/resources/installer/bin/unregister.bat $/unregister.bat COMMENT "Copying unregister.bat script.") # Copy files and other dependencies to target dir From 84140abbe05b4f6d9d9ad1a0fe3db8de1b52c22c Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sat, 26 Oct 2024 17:26:08 -0400 Subject: [PATCH 13/15] Moved configurations directory from `build\bin\Debug\configurations` to `build\bin\resources\configurations`. This change update the build directory structure to match the installation directory structure making the source code and install more alike. --- CMakeLists.txt | 1 + resources/configurations/shellanything.xml | 42 +++++++++++----------- src/CMakeLists.txt | 4 --- src/core/App.cpp | 19 ++++++++-- src/core/App.h | 13 +++++++ src/core/CMakeLists.txt | 2 +- src/core/PropertyManager.cpp | 5 ++- src/tests/TestConfigManager.cpp | 5 ++- src/tests/TestConfiguration.cpp | 38 ++++++++++++-------- src/tests/TestSelectionContext.cpp | 7 +++- 10 files changed, 87 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6ee691..b91c228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -378,6 +378,7 @@ install(FILES ${CMAKE_SOURCE_DIR}/resources/installer/bin/register.bat DESTINATION ${SHELLANYTHING_INSTALL_BIN_DIR}) # Resources +install(DIRECTORY "${CMAKE_SOURCE_DIR}/resources/configurations" DESTINATION ${SHELLANYTHING_INSTALL_RESOURCE_DIR}) install(DIRECTORY "${CMAKE_SOURCE_DIR}/resources/Windows Icon Tables" DESTINATION ${SHELLANYTHING_INSTALL_RESOURCE_DIR}) install(FILES ${CMAKE_SOURCE_DIR}/resources/icons/shellanything.ico ${CMAKE_SOURCE_DIR}/resources/icons/0.1.0.ico diff --git a/resources/configurations/shellanything.xml b/resources/configurations/shellanything.xml index 4ee24f5..6f55241 100644 --- a/resources/configurations/shellanything.xml +++ b/resources/configurations/shellanything.xml @@ -36,51 +36,51 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -136,22 +136,22 @@ - + - + - + - + @@ -159,22 +159,22 @@ - + - + - + - + @@ -183,7 +183,7 @@ - + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54fd1e4..14b2886 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,10 +48,6 @@ add_custom_target(src_dummy_target "${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in" ) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/resources/configurations - DESTINATION ${SHELLANYTHING_INSTALL_BIN_DIR} -) - install(FILES ${LIBMAGIC_MGC_DIR}/magic.mgc ${LIBMAGIC_MGC_DIR}/file.exe ${ZLIB_INCLUDE_DIRS}/../bin/zlib.dll diff --git a/src/core/App.cpp b/src/core/App.cpp index 5509f28..b15d8b4 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -233,6 +233,20 @@ namespace shellanything return config_dir; } + std::string App::GetBinDirectory() + { + const std::string module_path = GetCurrentModulePathUtf8(); + const std::string bin_dir = ra::filesystem::GetParentPath(module_path); + return bin_dir; + } + + std::string App::GetInstallDirectory() + { + const std::string bin_dir = GetBinDirectory(); + const std::string install_dir = ra::filesystem::GetParentPath(bin_dir); + return install_dir; + } + bool App::Start() { SetupGlobalProperties(); @@ -289,8 +303,7 @@ namespace shellanything void App::InstallDefaultConfigurations(const std::string& dest_dir) { - std::string app_path = GetCurrentModulePathUtf8(); - std::string app_dir = ra::filesystem::GetParentPath(app_path); + const std::string install_dir = shellanything::App::GetInstallDirectory(); static const char* default_files[] = { "default.xml", @@ -304,7 +317,7 @@ namespace shellanything for (size_t i = 0; i < num_files; i++) { const char* filename = default_files[i]; - std::string source_path = app_dir + "\\configurations\\" + filename; + std::string source_path = install_dir + "\\resources\\configurations\\" + filename; std::string target_path = dest_dir + "\\" + filename; SA_LOG(INFO) << "Installing configuration file: " << target_path; diff --git a/src/core/App.h b/src/core/App.h index 76e1be2..0878b8f 100644 --- a/src/core/App.h +++ b/src/core/App.h @@ -215,6 +215,19 @@ namespace shellanything /// Returns true if the directory is valid for reading configurations. Returns false otherwise. bool IsValidConfigDirectory(const std::string& path); + /// + /// Get the application's bin directory. The bin directory contains all ShellAnything's executables and binaries. + /// + /// Returns the path of the bin directory. + static std::string GetBinDirectory(); + + /// + /// Get the application's installation directory. The installation directory is the root directory selected when the application was installed. + /// When running in debug mode or within a development environment, the installation directory is set to the parent directory of `${CMAKE_BIN_DIR}\build\bin\Debug`. + /// + /// Returns the path of the installation directory. + static std::string GetInstallDirectory(); + /// /// Initialize and start the application ready for usage. /// diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9f056c3..13fd833 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -114,7 +114,7 @@ source_group("Default Configuration Files" FILES ${CONFIGURATION_DEFAUL # Copy default configuration files database to target dir add_custom_command( TARGET sa.core POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/resources/configurations $/configurations + ${CMAKE_SOURCE_DIR}/resources/configurations $/../resources/configurations COMMENT "Copying default configurations files database.") # Copy registration and unregistration scripts to target dir diff --git a/src/core/PropertyManager.cpp b/src/core/PropertyManager.cpp index 455f9ae..f15bf69 100644 --- a/src/core/PropertyManager.cpp +++ b/src/core/PropertyManager.cpp @@ -648,9 +648,8 @@ namespace shellanything shellanything::App& app = shellanything::App::GetInstance(); //define global properties - std::string prop_core_module_path = GetCurrentModulePathUtf8(); - std::string prop_application_directory = ra::filesystem::GetParentPath(prop_core_module_path); - std::string prop_install_directory = ra::filesystem::GetParentPath(prop_application_directory); + std::string prop_application_directory = app.GetBinDirectory(); + std::string prop_install_directory = app.GetInstallDirectory(); std::string prop_path_separator = ra::filesystem::GetPathSeparatorStr(); std::string prop_line_separator = ra::environment::GetLineSeparator(); diff --git a/src/tests/TestConfigManager.cpp b/src/tests/TestConfigManager.cpp index 2950c25..917b5c5 100644 --- a/src/tests/TestConfigManager.cpp +++ b/src/tests/TestConfigManager.cpp @@ -23,6 +23,7 @@ *********************************************************************************/ #include "TestConfigManager.h" +#include "App.h" #include "Workspace.h" #include "QuickLoader.h" #include "ConfigManager.h" @@ -338,7 +339,9 @@ namespace shellanything { ConfigManager& mgr = ConfigManager::GetInstance(); - const std::string path = "configurations/default.xml"; + const std::string install_dir = shellanything::App::GetInstallDirectory(); + + const std::string path = install_dir + "/resources/configurations/default.xml"; std::string error_message = ra::testing::GetTestQualifiedName(); //init error message to an unexpected string ConfigFile* config = ConfigFile::LoadFile(path, error_message); diff --git a/src/tests/TestConfiguration.cpp b/src/tests/TestConfiguration.cpp index e8b1ec5..8e4842c 100644 --- a/src/tests/TestConfiguration.cpp +++ b/src/tests/TestConfiguration.cpp @@ -23,11 +23,13 @@ *********************************************************************************/ #include "TestConfiguration.h" +#include "App.h" #include "Workspace.h" #include "ConfigManager.h" #include "ConfigFile.h" #include "Menu.h" #include "ActionExecute.h" +#include "SaUtils.h" #include "rapidassist/filesystem_utf8.h" #include "rapidassist/testing.h" @@ -94,17 +96,17 @@ namespace shellanything TEST_F(TestConfiguration, testIsValidConfigFile) { static const char* files[] = { - //default configuration files - "configurations\\default.xml", - "configurations\\Microsoft Office 2003.xml", - "configurations\\Microsoft Office 2007.xml", - "configurations\\Microsoft Office 2010.xml", - "configurations\\Microsoft Office 2013.xml", - "configurations\\Microsoft Office 2016.xml", - "configurations\\shellanything.xml", - "configurations\\WinDirStat.xml", - - //test configuration files + //default configuration files (relative to bin directory) + "..\\resources\\configurations\\default.xml", + "..\\resources\\configurations\\Microsoft Office 2003.xml", + "..\\resources\\configurations\\Microsoft Office 2007.xml", + "..\\resources\\configurations\\Microsoft Office 2010.xml", + "..\\resources\\configurations\\Microsoft Office 2013.xml", + "..\\resources\\configurations\\Microsoft Office 2016.xml", + "..\\resources\\configurations\\shellanything.xml", + "..\\resources\\configurations\\WinDirStat.xml", + + //test configuration files (relative to bin directory) "test_files\\samples.xml", "test_files\\TestConfigManager.testAssignCommandId.1.xml", "test_files\\TestConfigManager.testAssignCommandId.2.xml", @@ -129,10 +131,13 @@ namespace shellanything }; const size_t num_files = sizeof(files) / sizeof(files[0]); + const std::string bin_dir = shellanything::App::GetBinDirectory(); + //for each test files for (size_t i = 0; i < num_files; i++) { - const std::string path = files[i]; + const std::string relative_path = files[i]; + const std::string path = bin_dir + "\\" + relative_path; ASSERT_TRUE(ra::filesystem::FileExists(path.c_str())) << "File '" << path.c_str() << "' is not found."; ASSERT_TRUE(shellanything::ConfigFile::IsValidConfigFile(path)) << "The file '" << path.c_str() << "' is not a valid configuration file."; } @@ -141,7 +146,8 @@ namespace shellanything TEST_F(TestConfiguration, testIsValidConfigFileUtf8) { static const std::string separator = ra::filesystem::GetPathSeparatorStr(); - const std::string source_path = "configurations/default.xml"; + const std::string install_dir = shellanything::App::GetInstallDirectory(); + const std::string source_path = install_dir + "/resources/configurations/default.xml"; std::string target_path = ra::filesystem::GetTemporaryDirectory() + separator + ra::testing::GetTestQualifiedName() + ".psi_\xCE\xA8_psi.xml"; //copy default config to the new utf-8 path @@ -153,7 +159,8 @@ namespace shellanything //-------------------------------------------------------------------------------------------------- TEST_F(TestConfiguration, testLoadFile) { - const std::string path = "configurations/default.xml"; + const std::string install_dir = shellanything::App::GetInstallDirectory(); + const std::string path = install_dir + "/resources/configurations/default.xml"; std::string error_message = ra::testing::GetTestQualifiedName(); //init error message to an unexpected string ConfigFile* config = ConfigFile::LoadFile(path, error_message); @@ -168,8 +175,9 @@ namespace shellanything { //This test validates that Configuration::LoadFile() supports filename with utf-8 characters. + const std::string install_dir = shellanything::App::GetInstallDirectory(); static const std::string separator = ra::filesystem::GetPathSeparatorStr(); - const std::string source_path = "configurations/default.xml"; + const std::string source_path = install_dir + "/resources/configurations/default.xml"; std::string target_path = ra::filesystem::GetTemporaryDirectory() + separator + ra::testing::GetTestQualifiedName() + ".psi_\xCE\xA8_psi.xml"; //copy default config to the new utf-8 path diff --git a/src/tests/TestSelectionContext.cpp b/src/tests/TestSelectionContext.cpp index a58249e..14a6f8d 100644 --- a/src/tests/TestSelectionContext.cpp +++ b/src/tests/TestSelectionContext.cpp @@ -23,8 +23,11 @@ *********************************************************************************/ #include "TestSelectionContext.h" +#include "App.h" #include "SelectionContext.h" #include "PropertyManager.h" +#include "SaUtils.h" + #include "rapidassist/process.h" #include "rapidassist/filesystem.h" #include "rapidassist/testing.h" @@ -525,11 +528,13 @@ namespace shellanything { PropertyManager& pmgr = PropertyManager::GetInstance(); + const std::string install_dir = shellanything::App::GetInstallDirectory(); + SelectionContext context; #ifdef _WIN32 { StringList elements; - elements.push_back(ra::process::GetCurrentProcessDir() + "\\configurations\\default.xml"); + elements.push_back(install_dir + "\\resources\\configurations\\default.xml"); context.SetElements(elements); } #else From 0513e63abacd460c29afc0aef74f2e499a20a316 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sun, 27 Oct 2024 11:55:56 -0400 Subject: [PATCH 14/15] Moved the logs directory from `%USERPROFILE%\ShellAnything\Logs` to `%LOCALAPPDATA%\ShellAnything\logs`. #108 --- CHANGES | 4 +- UserManual.md | 4 +- src/core/App.cpp | 93 +++++++++++++++++++++++++++++++++------ src/core/App.h | 17 ++++++- src/core/LoggerHelper.cpp | 23 ++++++++++ src/core/LoggerHelper.h | 7 +++ 6 files changed, 128 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index e168403..c61131f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ Changes for 0.10.0 -* **Breaking change:** The _Configuration Files_ directory has moved from `%USERPROFILE%\ShellAnything` to `%USERPROFILE%\ShellAnything\configurations`. On first application launch, Configuration Files in the old directory will move to the new directory automatically. Other files in `%USERPROFILE%\ShellAnything` will not be moved. +* **Breaking change:** The _Configuration Files_ directory has moved from `%USERPROFILE%\ShellAnything` to `%USERPROFILE%\ShellAnything\configurations`. Configuration Files in the old directory will move to the new directory automatically on application first launch. Other files in `%USERPROFILE%\ShellAnything` will not be moved. +* **Breaking change:** The _logs_ directory has moved from `%USERPROFILE%\ShellAnything\Logs` to `%LOCALAPPDATA%\ShellAnything\logs`. The previous logs directory will be removed on application first launch. * ShellAnything has a new high-resolution logo icon! * Shellanything now features verbose logging mode and command line arguments debugging tools. * ShellAnything now packages icons from [icons8/flat-color-icons](https://github.com/icons8/flat-color-icons). @@ -8,6 +9,7 @@ Changes for 0.10.0 Fixes: * Fixed issue #6 : (twice) Right-click on a directory with Windows Explorer in the left panel shows the menus twice. * Fixed issue #31 : (twice) Error in logs for CContextMenu::GetCommandString() +* Fixed issue #108: Separate location for log files ? (and exclusions?) * Fixed issue #109: Implement default and verbose logging. * Fixed issue #110: Create a simple command line arguments debugging application. * Fixed issue #148: Can't uninstall. diff --git a/UserManual.md b/UserManual.md index 93575b3..8830761 100644 --- a/UserManual.md +++ b/UserManual.md @@ -2651,9 +2651,7 @@ ShellAnything provides logging support for troubleshooting and debugging command The logging directory is unique for each users of the system. -The log files are stored in `C:\Users\%USERNAME%\ShellAnything\Logs` directory where `%USERNAME%` matches your current login username. -For example, the user `JohnSmith` can find his own ShellAnything log files in directory `C:\Users\JohnSmith\ShellAnything\Logs`. - +The log files are stored in `%LOCALAPPDATA%\ShellAnything\logs` directory. For example, the user `JohnSmith` can find his own ShellAnything log files in directory `C:\Users\JohnSmith\AppData\Local\ShellAnything\logs`. ### Filename Format ### diff --git a/src/core/App.cpp b/src/core/App.cpp index b15d8b4..c37b655 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -163,6 +163,14 @@ namespace shellanything return false; } + std::string App::GetLegacyLogsDirectory() + { + //get home directory of the user + std::string home_dir = ra::user::GetHomeDirectoryUtf8(); + std::string legacy_dir = home_dir + "\\" + app_name + "\\Logs"; + return legacy_dir; + } + std::string App::GetLogDirectory() { //Issue #10 - Change the log directory if run from the unit tests executable @@ -195,21 +203,30 @@ namespace shellanything //This DLL is most probably executed by the shell (File Explorer). //By default, GLOG will output log files in %TEMP% directory. - //However, I prefer to use %USERPROFILE%\ShellAnything\logs - std::string log_dir = ra::user::GetHomeDirectoryUtf8(); - if (!log_dir.empty()) + + // Issue #108. Log files should be stored in %LOCALAPPDATA%\ShellAnything\logs + std::string localappdata_dir = ra::environment::GetEnvironmentVariableUtf8("LOCALAPPDATA"); + if (!localappdata_dir.empty() && ra::filesystem::DirectoryExistsUtf8(localappdata_dir.c_str())) + { + std::string log_dir = localappdata_dir + "\\" + app_name + "\\logs"; + if (IsValidLogDirectory(log_dir)) + return log_dir; + } + + // Fallback to %USERPROFILE%\ShellAnything\logs + std::string home_dir = ra::user::GetHomeDirectoryUtf8(); + if (!home_dir.empty() && ra::filesystem::DirectoryExistsUtf8(home_dir.c_str())) { //We got the %USERPROFILE% directory. - //Now add our custom path to it - log_dir.append("\\" + app_name + "\\logs"); + std::string log_dir = home_dir + "\\" + app_name + "\\logs"; if (IsValidLogDirectory(log_dir)) return log_dir; } //Failed getting HOME directory. //Fallback to using %TEMP%. - log_dir = ra::environment::GetEnvironmentVariableUtf8("TEMP"); - return log_dir; + std::string temp_dir = ra::environment::GetEnvironmentVariableUtf8("TEMP"); + return temp_dir; } std::string App::GetLegacyConfigurationsDirectory() @@ -329,16 +346,17 @@ namespace shellanything } } - void App::ClearLegacyConfigurationDirectory(const std::string& legacy_dir) + void App::ClearLegacyConfigurationDirectory() { + const std::string legacy_config_dir = GetLegacyConfigurationsDirectory(); const std::string config_dir = GetConfigurationsDirectory(); - if (legacy_dir == config_dir) + if (legacy_config_dir == config_dir) return; // nothing to do // Search for xml files directly under legacy_dir ra::strings::StringVector files; static const int depth = 0; // Do not search recursively - bool success = ra::filesystem::FindFilesUtf8(files, legacy_dir.c_str(), depth); + bool success = ra::filesystem::FindFilesUtf8(files, legacy_config_dir.c_str(), depth); if (!success) return; // aborted @@ -366,18 +384,65 @@ namespace shellanything } } + void App::ClearLegacyLogsDirectory() + { + const std::string legacy_logs_dir = GetLegacyLogsDirectory(); + + // Search for log files directly under legacy_logs_dir + ra::strings::StringVector files; + static const int depth = 0; // Do not search recursively + bool success = ra::filesystem::FindFilesUtf8(files, legacy_logs_dir.c_str(), depth); + if (!success) + return; // aborted + + // for each file found + for (size_t i = 0; i < files.size(); i++) + { + const std::string& file_path = files[i]; + + // Is that a configuration file ? + if (LoggerHelper::IsValidLogFile(file_path)) + { + // It does not belongs there. + // Delete the file + SA_LOG(INFO) << "Deleting old legacy log file '" << file_path << "'."; + bool deleted = ra::filesystem::DeleteFileUtf8(file_path.c_str()); + if (!deleted) + { + SA_LOG(ERROR) << "Failed deleting old legacy log file '" << file_path << "'."; + } + } + } + + // Check if the directory is empty. + // We need to make this check before calling ra::filesystel::DeleteDirectory() because the + // DeleteDirectory function will automatically delete remaining files in order to delete the directory. + // We need to make sure the directory is empty first. + bool empty = ra::filesystem::IsDirectoryEmptyUtf8(legacy_logs_dir); + if (empty) + { + // Now it is safe to delete the directory + SA_LOG(INFO) << "Deleting old legacy log directory '" << legacy_logs_dir << "'."; + ra::filesystem::DeleteDirectoryUtf8(legacy_logs_dir.c_str()); + } + else + { + SA_LOG(ERROR) << "Skipped deleting old legacy log directory '" << legacy_logs_dir << "'. The directory is not empty. The directory likely contains files that are not log files."; + } + } + void App::InitConfigManager() { shellanything::ConfigManager& cmgr = shellanything::ConfigManager::GetInstance(); - std::string legacy_dir = GetLegacyConfigurationsDirectory(); std::string config_dir = GetConfigurationsDirectory(); bool first_run = IsFirstApplicationRun(app_name, app_version); if (first_run) { SA_LOG(INFO) << "First application launch."; - ClearLegacyConfigurationDirectory(legacy_dir); // Issue #108 moved Configuration Files directory to a new location. + ClearLegacyConfigurationDirectory(); // Issue #108 moved Configuration Files directory to a new location. + ClearLegacyLogsDirectory(); // Issue #108 delete previous logs directory. InstallDefaultConfigurations(config_dir); } @@ -394,10 +459,10 @@ namespace shellanything //get home directory of the user std::string home_dir = ra::user::GetHomeDirectoryUtf8(); std::string config_dir = GetConfigurationsDirectory(); - std::string log_dir = ra::unicode::AnsiToUtf8(GetLogDirectory()); + std::string log_dir = GetLogDirectory(); SA_LOG(INFO) << "HOME directory : " << home_dir.c_str(); - SA_LOG(INFO) << "Config directory : " << config_dir.c_str(); + SA_LOG(INFO) << "CONFIG directory : " << config_dir.c_str(); SA_LOG(INFO) << "LOG directory : " << log_dir.c_str(); //define global properties diff --git a/src/core/App.h b/src/core/App.h index 0878b8f..ad5f777 100644 --- a/src/core/App.h +++ b/src/core/App.h @@ -182,6 +182,12 @@ namespace shellanything /// Returns true if application is loaded in a test environment. Returns false otherwise. bool IsTestingEnvironment(); + /// + /// Get the application's legacy log directory. The returned directory has write access. + /// + /// Returns the path of the legacy directory that was used by the logging framework. + std::string GetLegacyLogsDirectory(); + /// /// Get the application's log directory. The returned directory has write access. /// @@ -242,9 +248,16 @@ namespace shellanything void InstallDefaultConfigurations(const std::string& dest_dir); /// - /// Moved any Configuration Files from the given legacy directory to the official configurations directory. + /// Moved any Configuration Files from the legacy directory to the official configurations directory. + /// The term legacy refers to version 0.9.0 and older. + /// + void ClearLegacyConfigurationDirectory(); + + /// + /// Delete any log files from the legacy logs directory. + /// The term legacy refers to version 0.9.0 and older. /// - void ClearLegacyConfigurationDirectory(const std::string& legacy_dir); + void ClearLegacyLogsDirectory(); /// /// Initialize the Configuration Manager to the user's stall the original configuration files to the specified destination directory. diff --git a/src/core/LoggerHelper.cpp b/src/core/LoggerHelper.cpp index db3be37..5a0b10e 100644 --- a/src/core/LoggerHelper.cpp +++ b/src/core/LoggerHelper.cpp @@ -30,6 +30,8 @@ #include #include "rapidassist/strings.h" +#include "rapidassist/filesystem_utf8.h" +#include "rapidassist/unicode.h" namespace shellanything { @@ -127,6 +129,27 @@ namespace shellanything return has_vebose_logging; } + bool LoggerHelper::IsValidLogFile(const std::string& path) + { + std::string file_extension = ra::filesystem::GetFileExtention(path); + file_extension = ra::strings::Uppercase(file_extension); + + if (file_extension != "LOG") + return false; + + // Peek at the file for validating content + std::string data; + bool peeked = ra::filesystem::PeekFileUtf8(path, 1024 * 1024, data); + if (!peeked) + return false; + + bool valid = ra::unicode::IsValidUtf8(data.c_str()); + if (!valid) + return false; // the file might contain binary data + + return true; + } + // ------------------------------------------------------------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/core/LoggerHelper.h b/src/core/LoggerHelper.h index b304740..5c990af 100644 --- a/src/core/LoggerHelper.h +++ b/src/core/LoggerHelper.h @@ -66,6 +66,13 @@ namespace shellanything /// Returns true when verbose logging is enabled. Returns false otherwise. static bool IsVerboseLoggingEnabled(); + /// + /// Detect if a given file is a valid log file. + /// + /// The file path to check + /// Returns true if the file is a valid log file. Returns false otherwise. + static bool IsValidLogFile(const std::string& path); + private: ILoggerService::LOG_LEVEL mLevel; bool mIsVerboseStream; From a6777880ca3e7c09400cf5fe0d0986ac753539c6 Mon Sep 17 00:00:00 2001 From: Antoine Beauchamp Date: Sun, 27 Oct 2024 13:22:07 -0400 Subject: [PATCH 15/15] Implemented the following environment variable options: * `SA_OPTION_CONFIGURATIONS_DIR`: Set to a custom value to change/override the directory where Configuration Files are stored. * `SA_OPTION_LOGS_DIR`: Set to a custom value to change/override the directory where Log Files are stored. #108 --- UserManual.md | 20 +++++++++++++------- src/core/App.cpp | 19 +++++++++++++++++++ src/core/Environment.cpp | 8 ++++++++ src/core/Environment.h | 17 +++++++++++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/UserManual.md b/UserManual.md index 8830761..b3d3568 100644 --- a/UserManual.md +++ b/UserManual.md @@ -46,7 +46,7 @@ This manual includes a description of the system functionalities and capabilitie * [Multi-selection-based properties](#multi-selection-based-properties) * [Fixed properties](#fixed-properties) * [Default properties](#default-properties) -* [Environment variables](#environment-variables) +* [Environment Variables options](#environment-variables-options) * [Tools](#tools) * [file_explorer_renew](#file_explorer_renew) * [arguments.debugger](#argumentsdebugger) @@ -169,7 +169,7 @@ A *configuration file* contains the definition of all [<menu>](#Menu) elem When a user right-click on a file in *Windows Explorer*, the application will load all available *configuration files* and display their content into the displayed context menu. -The list of *Configuration Files* is unique for each users of the system. The files are stored in `C:\Users\%USERNAME%\ShellAnything\configurations` directory where `%USERNAME%` is your current Windows session *username*. Note that *Windows Explorer* also support copy & pasting `C:\Users\%USERNAME%\ShellAnything\configurations` into an *address bar* to quickly jump to the directory. +The list of *Configuration Files* is unique for each users of the system. The files are stored in `C:\Users\%USERNAME%\ShellAnything\configurations` directory where `%USERNAME%` is your current Windows session *username*. Note that you can paste `C:\Users\%USERNAME%\ShellAnything\configurations` into an *address bar* of *Windows Explorer* to quickly jump to the directory. The application support multiple *configuration files* at the same time. One can add new files in the *configuration directory* and the system will automatically detect and load them. @@ -177,6 +177,8 @@ When a *configuration file* is deleted, the application automatically detect the To temporary disable a *configuration file*, one can simply change the file extension from `xml` to `txt`. Change the file extension back to `xml` to re-enable the file. +**Note:** The *Configuration Files* directory can be modified with the `SA_OPTION_CONFIGURATIONS_DIR` environment variable option. See [Environment Variables options](#environment-variables-options) section for details. + ## Basic Xml Document ## @@ -1822,7 +1824,7 @@ For example, the following would define `services.wce.command.start` and `servic -# Environment variables # +# Environment Variables options # ShellAnything default startup behavior can be modified by setting specific pre-defined environment variables. Some features or configuration options can also be enabled or disabled through environment variables. For example, one can define an environment variables to enable verbose logging. @@ -1834,10 +1836,11 @@ All ShellAnything environment variables names are prefixed with `SA_`. The following table defines the list of pre-defined environment variables for ShellAnything: -| Name | Description | -|--------------------------------|--------------------------------------------------------------------------------------------------------------------| -| SA_OPTION_LOGGING_VERBOSE | Enables [verbose logging](#verbose-logging) when set to a value that evaluates to [true](#istrue-attribute). | - +| Name | Description | +|--------------------------------|----------------------------------------------------------------------------------------------------------------------| +| SA_OPTION_LOGGING_VERBOSE | Enables [verbose logging](#verbose-logging) when set to a value that evaluates to [true](#istrue-attribute). | +| SA_OPTION_CONFIGURATIONS_DIR | Set to a custom value to change/override the directory where [Configuration Files](#configuration-files) are stored. | +| SA_OPTION_LOGS_DIR | Set to a custom value to change/override the directory where [Log Files](#logging-support) are stored. | @@ -2653,6 +2656,9 @@ The logging directory is unique for each users of the system. The log files are stored in `%LOCALAPPDATA%\ShellAnything\logs` directory. For example, the user `JohnSmith` can find his own ShellAnything log files in directory `C:\Users\JohnSmith\AppData\Local\ShellAnything\logs`. +**Note:** The logging directory can be modified with the `SA_OPTION_LOGS_DIR` environment variable option. See [Environment Variables options](#environment-variables-options) section for details. + + ### Filename Format ### diff --git a/src/core/App.cpp b/src/core/App.cpp index c37b655..dd37b8b 100644 --- a/src/core/App.cpp +++ b/src/core/App.cpp @@ -26,6 +26,7 @@ #include "LoggerHelper.h" #include "ConfigManager.h" #include "PropertyManager.h" +#include "Environment.h" #include "rapidassist/process_utf8.h" #include "rapidassist/user_utf8.h" @@ -204,6 +205,15 @@ namespace shellanything //By default, GLOG will output log files in %TEMP% directory. + // Issue #108. Log files directory can be overriden with an option. + Environment& env = Environment::GetInstance(); + if (env.IsOptionSet(Environment::SYSTEM_LOGS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME)) + { + std::string log_dir = env.GetOptionValue(Environment::SYSTEM_LOGS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME); + if (IsValidLogDirectory(log_dir)) + return log_dir; + } + // Issue #108. Log files should be stored in %LOCALAPPDATA%\ShellAnything\logs std::string localappdata_dir = ra::environment::GetEnvironmentVariableUtf8("LOCALAPPDATA"); if (!localappdata_dir.empty() && ra::filesystem::DirectoryExistsUtf8(localappdata_dir.c_str())) @@ -239,6 +249,15 @@ namespace shellanything std::string App::GetConfigurationsDirectory() { + // Issue #108. Configuration Files directory can be overriden with an option. + Environment& env = Environment::GetInstance(); + if (env.IsOptionSet(Environment::SYSTEM_CONFIGURATIONS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME)) + { + std::string config_dir = env.GetOptionValue(Environment::SYSTEM_CONFIGURATIONS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME); + if (IsValidConfigDirectory(config_dir)) + return config_dir; + } + //get home directory of the user std::string home_dir = ra::user::GetHomeDirectoryUtf8(); std::string app_dir = home_dir + "\\" + app_name; diff --git a/src/core/Environment.cpp b/src/core/Environment.cpp index 60de92f..f96104b 100644 --- a/src/core/Environment.cpp +++ b/src/core/Environment.cpp @@ -33,6 +33,8 @@ namespace shellanything static const std::string EMPTY_VALUE; const std::string Environment::SYSTEM_LOGGING_VERBOSE_ENVIRONMENT_VARIABLE_NAME = "SA_OPTION_LOGGING_VERBOSE"; + const std::string Environment::SYSTEM_CONFIGURATIONS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME = "SA_OPTION_CONFIGURATIONS_DIR"; + const std::string Environment::SYSTEM_LOGS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME = "SA_OPTION_LOGS_DIR"; Environment::Environment() { @@ -67,6 +69,12 @@ namespace shellanything return is_true; } + std::string Environment::GetOptionValue(const std::string& name) const + { + std::string value = ra::environment::GetEnvironmentVariableUtf8(name.c_str()); + return value; + } + bool Environment::IsOptionFalse(const std::string& name) const { std::string value = ra::environment::GetEnvironmentVariableUtf8(name.c_str()); diff --git a/src/core/Environment.h b/src/core/Environment.h index ffda150..154470b 100644 --- a/src/core/Environment.h +++ b/src/core/Environment.h @@ -54,6 +54,16 @@ namespace shellanything /// static const std::string SYSTEM_LOGGING_VERBOSE_ENVIRONMENT_VARIABLE_NAME; + /// + /// Name of the environment variable that defines the configurations directory path override. + /// + static const std::string SYSTEM_CONFIGURATIONS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME; + + /// + /// Name of the environment variable that defines the logs directory path override. + /// + static const std::string SYSTEM_LOGS_DIR_OVERRIDE_ENVIRONMENT_VARIABLE_NAME; + public: /// @@ -64,6 +74,13 @@ namespace shellanything /// Returns true if the environment variable is set. Returns false otherwise. bool IsOptionSet(const std::string& name) const; + /// + /// Get the value of an option. + /// + /// The name of the environment variable to check. + /// Returns the value of the given environment variable. + std::string GetOptionValue(const std::string& name) const; + /// /// Check if an environment variable evaluates to true. /// An empty environment variable value returns false.