NSIS: Register placeholder extension
authorChristian Kamm <mail@ckamm.de>
Wed, 21 Mar 2018 11:23:31 +0000 (12:23 +0100)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 15 Dec 2020 09:57:50 +0000 (10:57 +0100)
Also change the placeholder suffix config option to not include the dot,
the dotless form is needed in the nsis script.

CMakeLists.txt
NEXTCLOUD.cmake
admin/win/nsi/lib/fileassoc.nsh [new file with mode: 0644]
cmake/modules/NSIS.template.in
config.h.in
src/gui/application.cpp
src/gui/folder.cpp
src/gui/owncloud.xml.in
src/gui/socketapi.cpp

index 0062e0beb467215c53e935ac16968851d0341034..5df23883895d25f5427d686ed877882986f7845e 100644 (file)
@@ -180,8 +180,6 @@ if(APPLE)
   set( SOCKETAPI_TEAM_IDENTIFIER_PREFIX "" CACHE STRING "SocketApi prefix (including a following dot) that must match the codesign key's TeamIdentifier/Organizational Unit" )
 endif()
 
-set(OWNCLOUD_PLACEHOLDER_SUFFIX ".owncloud" CACHE STRING "Placeholder suffix (must start with a .)")
-
 if(BUILD_CLIENT)
     if(APPLE AND BUILD_UPDATER)
         find_package(Sparkle)
index ec5830a4f62b6b7a26955c111d2454d60fb4971b..870cad83ed83611d5a36e488d7fac40867bbf41d 100644 (file)
@@ -9,6 +9,7 @@ set( APPLICATION_ICON_NAME  "Nextcloud" )
 set( APPLICATION_SERVER_URL "" CACHE STRING "URL for the server to use. If entered, the UI field will be pre-filled with it" )
 set( APPLICATION_SERVER_URL_ENFORCE ON ) # If set and APPLICATION_SERVER_URL is defined, the server can only connect to the pre-defined URL
 set( APPLICATION_REV_DOMAIN "com.nextcloud.desktopclient" )
+set( APPLICATION_PLACEHOLDER_SUFFIX "nextcloud" CACHE STRING "Placeholder suffix (not including the .)")
 
 set( LINUX_PACKAGE_SHORTNAME "nextcloud" )
 set( LINUX_APPLICATION_ID "${APPLICATION_REV_DOMAIN}.${LINUX_PACKAGE_SHORTNAME}")
diff --git a/admin/win/nsi/lib/fileassoc.nsh b/admin/win/nsi/lib/fileassoc.nsh
new file mode 100644 (file)
index 0000000..87e6caf
--- /dev/null
@@ -0,0 +1,120 @@
+; fileassoc.nsh
+; File association helper macros
+; Written by Saivert
+; See http://nsis.sourceforge.net/FileAssoc
+;
+; Features automatic backup system and UPDATEFILEASSOC macro for
+; shell change notification.
+;
+; |> How to use <|
+; To associate a file with an application so you can double-click it in explorer, use
+; the APP_ASSOCIATE macro like this:
+;
+;   Example:
+;   !insertmacro APP_ASSOCIATE "txt" "myapp.textfile" "Description of txt files" \
+;     "$INSTDIR\myapp.exe,0" "Open with myapp" "$INSTDIR\myapp.exe $\"%1$\""
+;
+; Never insert the APP_ASSOCIATE macro multiple times, it is only ment
+; to associate an application with a single file and using the
+; the "open" verb as default. To add more verbs (actions) to a file
+; use the APP_ASSOCIATE_ADDVERB macro.
+;
+;   Example:
+;   !insertmacro APP_ASSOCIATE_ADDVERB "myapp.textfile" "edit" "Edit with myapp" \
+;     "$INSTDIR\myapp.exe /edit $\"%1$\""
+;
+; To have access to more options when registering the file association use the
+; APP_ASSOCIATE_EX macro. Here you can specify the verb and what verb is to be the
+; standard action (default verb).
+;
+; And finally: To remove the association from the registry use the APP_UNASSOCIATE
+; macro. Here is another example just to wrap it up:
+;   !insertmacro APP_UNASSOCIATE "txt" "myapp.textfile"
+;
+; |> Note <|
+; When defining your file class string always use the short form of your application title
+; then a period (dot) and the type of file. This keeps the file class sort of unique.
+;   Examples:
+;   Winamp.Playlist
+;   NSIS.Script
+;   Photoshop.JPEGFile
+;
+; |> Tech info <|
+; The registry key layout for a file association is:
+; HKEY_CLASSES_ROOT
+;     <applicationID> = <"description">
+;         shell
+;             <verb> = <"menu-item text">
+;                 command = <"command string">
+;
+
+!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
+  ; Backup the previously associated file class
+  ReadRegStr $R0 HKCR ".${EXT}" ""
+  WriteRegStr HKCR ".${EXT}" "${FILECLASS}_backup" "$R0"
+
+  WriteRegStr HKCR ".${EXT}" "" "${FILECLASS}"
+
+  WriteRegStr HKCR "${FILECLASS}" "" `${DESCRIPTION}`
+  WriteRegStr HKCR "${FILECLASS}\DefaultIcon" "" `${ICON}`
+  WriteRegStr HKCR "${FILECLASS}\shell" "" "open"
+  WriteRegStr HKCR "${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
+  WriteRegStr HKCR "${FILECLASS}\shell\open\command" "" `${COMMAND}`
+!macroend
+
+!macro APP_ASSOCIATE_EX EXT FILECLASS DESCRIPTION ICON VERB DEFAULTVERB SHELLNEW COMMANDTEXT COMMAND
+  ; Backup the previously associated file class
+  ReadRegStr $R0 HKCR ".${EXT}" ""
+  WriteRegStr HKCR ".${EXT}" "${FILECLASS}_backup" "$R0"
+
+  WriteRegStr HKCR ".${EXT}" "" "${FILECLASS}"
+  StrCmp "${SHELLNEW}" "0" +2
+  WriteRegStr HKCR ".${EXT}\ShellNew" "NullFile" ""
+
+  WriteRegStr HKCR "${FILECLASS}" "" `${DESCRIPTION}`
+  WriteRegStr HKCR "${FILECLASS}\DefaultIcon" "" `${ICON}`
+  WriteRegStr HKCR "${FILECLASS}\shell" "" `${DEFAULTVERB}`
+  WriteRegStr HKCR "${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}`
+  WriteRegStr HKCR "${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}`
+!macroend
+
+!macro APP_ASSOCIATE_ADDVERB FILECLASS VERB COMMANDTEXT COMMAND
+  WriteRegStr HKCR "${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}`
+  WriteRegStr HKCR "${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}`
+!macroend
+
+!macro APP_ASSOCIATE_REMOVEVERB FILECLASS VERB
+  DeleteRegKey HKCR `${FILECLASS}\shell\${VERB}`
+!macroend
+
+
+!macro APP_UNASSOCIATE EXT FILECLASS
+  ; Backup the previously associated file class
+  ReadRegStr $R0 HKCR ".${EXT}" `${FILECLASS}_backup`
+  WriteRegStr HKCR ".${EXT}" "" "$R0"
+
+  DeleteRegKey HKCR `${FILECLASS}`
+!macroend
+
+!macro APP_ASSOCIATE_GETFILECLASS OUTPUT EXT
+  ReadRegStr ${OUTPUT} HKCR ".${EXT}" ""
+!macroend
+
+
+; !defines for use with SHChangeNotify
+!ifdef SHCNE_ASSOCCHANGED
+!undef SHCNE_ASSOCCHANGED
+!endif
+!define SHCNE_ASSOCCHANGED 0x08000000
+!ifdef SHCNF_FLUSH
+!undef SHCNF_FLUSH
+!endif
+!define SHCNF_FLUSH        0x1000
+
+!macro UPDATEFILEASSOC
+; Using the system.dll plugin to call the SHChangeNotify Win32 API function so we
+; can update the shell.
+  System::Call "shell32::SHChangeNotify(i,i,i,i) (${SHCNE_ASSOCCHANGED}, ${SHCNF_FLUSH}, 0, 0)"
+!macroend
+
+;EOF
index 7dcb0af87dbfcf67f29d746f36c9d69ab2982432..982faf3f14ecedf09b10984103e2adea4984ed13 100644 (file)
@@ -7,6 +7,8 @@
 !define APPLICATION_CMD_EXECUTABLE "@APPLICATION_EXECUTABLE@cmd.exe"
 !define APPLICATION_DOMAIN "@APPLICATION_DOMAIN@"
 !define APPLICATION_LICENSE "@APPLICATION_LICENSE@"
+!define APPLICATION_PLACEHOLDER_SUFFIX "@APPLICATION_PLACEHOLDER_SUFFIX@"
+!define APPLICATION_PLACEHOLDER_FILECLASS "@APPLICATION_EXECUTABLE@.@APPLICATION_PLACEHOLDER_SUFFIX@"
 !define WIN_SETUP_BITMAP_PATH "@WIN_SETUP_BITMAP_PATH@"
 
 !define CRASHREPORTER_EXECUTABLE "@CRASHREPORTER_EXECUTABLE@"
@@ -100,6 +102,8 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll"
 !include Library.nsh ;Used by the COM registration for shell extensions
 !include x64.nsh ;Used to determine the right arch for the shell extensions
 
+!include ${source_path}/admin/win/nsi/lib/fileassoc.nsh
+
 ;-----------------------------------------------------------------------------
 ; Memento selections stored in registry.
 ;-----------------------------------------------------------------------------
@@ -469,6 +473,9 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
    ;CSync configs
    File "${SOURCE_PATH}/sync-exclude.lst"
 
+   ;Add file association
+   !insertmacro APP_ASSOCIATE "${APPLICATION_PLACEHOLDER_SUFFIX}" "${APPLICATION_PLACEHOLDER_FILECLASS}" "Placeholder for Remote File" "$INSTDIR\${APPLICATION_EXECUTABLE},0" "Download" "$INSTDIR\${APPLICATION_EXECUTABLE} $\"%1$\""
+
 SectionEnd
 
 !ifdef OPTION_SECTION_SC_SHELL_EXT
@@ -646,6 +653,9 @@ Section Uninstall
 
    DeleteRegKey HKCR "${APPLICATION_NAME}"
 
+   ;Remove file association
+   !insertmacro APP_UNASSOCIATE "${APPLICATION_PLACEHOLDER_SUFFIX}" "${APPLICATION_PLACEHOLDER_FILECLASS}"
+
    ;Shell extension
    !ifdef OPTION_SECTION_SC_SHELL_EXT
       !define LIBRARY_COM
index 0983b6ba1af70e4f5e4cb116f297bd89a96ec832..f6074b166a9b02b8dfcddbd57953ccf67f6fd3e1 100644 (file)
@@ -26,7 +26,8 @@
 #cmakedefine APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR "@APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR@"
 #cmakedefine APPLICATION_WIZARD_HEADER_TITLE_COLOR "@APPLICATION_WIZARD_HEADER_TITLE_COLOR@"
 #cmakedefine APPLICATION_WIZARD_USE_CUSTOM_LOGO "@APPLICATION_WIZARD_USE_CUSTOM_LOGO@"
-#cmakedefine OWNCLOUD_PLACEHOLDER_SUFFIX "@OWNCLOUD_PLACEHOLDER_SUFFIX@"
+#cmakedefine APPLICATION_PLACEHOLDER_SUFFIX "@APPLICATION_PLACEHOLDER_SUFFIX@"
+#define APPLICATION_DOTPLACEHOLDER_SUFFIX "." APPLICATION_PLACEHOLDER_SUFFIX
 
 #cmakedefine ZLIB_FOUND @ZLIB_FOUND@
 
index d7be0774011f713ce823096c22d8fd425b215728..77bfaeef0ca6f26eebb450d908462445c0c1b860 100644 (file)
@@ -193,7 +193,7 @@ Application::Application(int &argc, char **argv)
         AbstractNetworkJob::httpTimeout = cfg.timeout();
 
     ExcludedFiles::setupPlaceholderExclude(
-        cfg.excludeFile(ConfigFile::UserScope), OWNCLOUD_PLACEHOLDER_SUFFIX);
+        cfg.excludeFile(ConfigFile::UserScope), APPLICATION_DOTPLACEHOLDER_SUFFIX);
 
     _folderManager.reset(new FolderMan);
 
@@ -503,7 +503,7 @@ void Application::parseOptions(const QStringList &options)
             _backgroundMode = true;
         } else if (option == QLatin1String("--version") || option == QLatin1String("-v")) {
             _versionOnly = true;
-        } else if (option.endsWith(QStringLiteral(OWNCLOUD_PLACEHOLDER_SUFFIX))) {
+        } else if (option.endsWith(QStringLiteral(APPLICATION_DOTPLACEHOLDER_SUFFIX))) {
             // placeholder file, open it after the Folder were created (if the app is not terminated)
             QTimer::singleShot(0, this, [this, option] { openPlaceholder(option); });
         } else {
@@ -683,7 +683,7 @@ void Application::slotGuiIsShowingSettings()
 
 void Application::openPlaceholder(const QString &filename)
 {
-    QString placeholderExt = QStringLiteral(OWNCLOUD_PLACEHOLDER_SUFFIX);
+    QString placeholderExt = QStringLiteral(APPLICATION_DOTPLACEHOLDER_SUFFIX);
     if (!filename.endsWith(placeholderExt)) {
         qWarning(lcApplication) << "Can only handle file ending in .owncloud. Unable to open" << filename;
         return;
index f724a85c3d80b1bef42394e02c1f3e18657beaad..9fdc4cfaa4acce18d87a90622011c7d4667d0d8b 100644 (file)
@@ -715,7 +715,7 @@ void Folder::setSyncOptions()
     opt._confirmExternalStorage = cfgFile.confirmExternalStorage();
     opt._moveFilesToTrash = cfgFile.moveToTrash();
     opt._newFilesArePlaceholders = _definition.usePlaceholders;
-    opt._placeholderSuffix = QStringLiteral(OWNCLOUD_PLACEHOLDER_SUFFIX);
+    opt._placeholderSuffix = QStringLiteral(APPLICATION_DOTPLACEHOLDER_SUFFIX);
 
     QByteArray chunkSizeEnv = qgetenv("OWNCLOUD_CHUNK_SIZE");
     if (!chunkSizeEnv.isEmpty()) {
index 5d1d062340a1bbdbb5db52c3c053c985a5fb8c7c..8bc9b48fe0863311e2a0ae6ed8906a62afdce6cd 100644 (file)
@@ -2,6 +2,6 @@
 <mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
   <mime-type type="application/x-@APPLICATION_EXECUTABLE@">
     <comment>@APPLICATION_NAME@ placeholders</comment>
-    <glob pattern="*@OWNCLOUD_PLACEHOLDER_SUFFIX@"/>
+    <glob pattern="*.@APPLICATION_PLACEHOLDER_SUFFIX@"/>
   </mime-type>
 </mime-info>
index 4ab9bead7951ac6b7ce91a6ff42d6b48d2ae0e5a..50ac868b09189ad1d143320476c9d10e8ec4d3d3 100644 (file)
@@ -689,7 +689,7 @@ void SocketApi::command_OPEN_PRIVATE_LINK(const QString &localFile, SocketListen
 void SocketApi::command_DOWNLOAD_PLACEHOLDER(const QString &filesArg, SocketListener *)
 {
     QStringList files = filesArg.split(QLatin1Char('\x1e')); // Record Separator
-    auto placeholderSuffix = QStringLiteral(OWNCLOUD_PLACEHOLDER_SUFFIX);
+    auto placeholderSuffix = QStringLiteral(APPLICATION_DOTPLACEHOLDER_SUFFIX);
 
     for (const auto &file : files) {
         if (!file.endsWith(placeholderSuffix))
@@ -974,7 +974,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
 
     // Placeholder download action
     if (syncFolder) {
-        auto placeholderSuffix = QStringLiteral(OWNCLOUD_PLACEHOLDER_SUFFIX);
+        auto placeholderSuffix = QStringLiteral(APPLICATION_DOTPLACEHOLDER_SUFFIX);
         bool hasPlaceholderFile = false;
         for (const auto &file : files) {
             if (file.endsWith(placeholderSuffix))