f61c6db6ff6a545970a241779847cc0732089125
[idea/community.git] / platform / configuration-store-impl / src / ImportSettingsAction.kt
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.ide.actions
17
18 import com.intellij.ide.IdeBundle
19 import com.intellij.ide.plugins.PluginManager
20 import com.intellij.ide.startup.StartupActionScriptManager
21 import com.intellij.openapi.actionSystem.AnAction
22 import com.intellij.openapi.actionSystem.AnActionEvent
23 import com.intellij.openapi.actionSystem.PlatformDataKeys
24 import com.intellij.openapi.application.ApplicationManager
25 import com.intellij.openapi.application.ApplicationNamesInfo
26 import com.intellij.openapi.application.PathManager
27 import com.intellij.openapi.application.ex.ApplicationEx
28 import com.intellij.openapi.project.DumbAware
29 import com.intellij.openapi.ui.Messages
30 import com.intellij.openapi.updateSettings.impl.UpdateSettings
31 import com.intellij.openapi.util.io.FileUtil
32 import com.intellij.openapi.util.io.getParentPath
33 import com.intellij.util.systemIndependentPath
34 import gnu.trove.THashSet
35 import java.io.File
36 import java.io.IOException
37 import java.io.InputStream
38 import java.nio.file.Paths
39 import java.util.zip.ZipException
40 import java.util.zip.ZipInputStream
41
42 private class ImportSettingsAction : AnAction(), DumbAware {
43   override fun actionPerformed(e: AnActionEvent) {
44     val dataContext = e.dataContext
45     val component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext)
46     ChooseComponentsToExportDialog.chooseSettingsFile(PathManager.getConfigPath(), component, IdeBundle.message("title.import.file.location"), IdeBundle.message("prompt.choose.import.file.path"))
47       .done {
48         val saveFile = File(it)
49         try {
50           doImport(saveFile)
51         }
52         catch (e1: ZipException) {
53           Messages.showErrorDialog(
54             IdeBundle.message("error.reading.settings.file", presentableFileName(saveFile), e1.message, promptLocationMessage()),
55             IdeBundle.message("title.invalid.file"))
56         }
57         catch (e1: IOException) {
58           Messages.showErrorDialog(IdeBundle.message("error.reading.settings.file.2", presentableFileName(saveFile), e1.message),
59             IdeBundle.message("title.error.reading.file"))
60         }
61       }
62   }
63
64   private fun doImport(saveFile: File) {
65     if (!saveFile.exists()) {
66       Messages.showErrorDialog(IdeBundle.message("error.cannot.find.file", presentableFileName(saveFile)),
67         IdeBundle.message("title.file.not.found"))
68       return
69     }
70
71     val relativePaths = getPaths(saveFile.inputStream())
72     if (!relativePaths.contains(ImportSettingsFilenameFilter.SETTINGS_JAR_MARKER)) {
73       Messages.showErrorDialog(
74         IdeBundle.message("error.file.contains.no.settings.to.import", presentableFileName(saveFile), promptLocationMessage()),
75         IdeBundle.message("title.invalid.file"))
76       return
77     }
78
79     val configPath = FileUtil.toSystemIndependentName(PathManager.getConfigPath())
80     val dialog = ChooseComponentsToExportDialog(getExportableComponentsMap(false, true, onlyPaths = relativePaths), false,
81       IdeBundle.message("title.select.components.to.import"),
82       IdeBundle.message("prompt.check.components.to.import"))
83     if (!dialog.showAndGet()) {
84       return
85     }
86
87     val tempFile = File(PathManager.getPluginTempPath(), saveFile.name)
88     FileUtil.copy(saveFile, tempFile)
89     val filenameFilter = ImportSettingsFilenameFilter(getRelativeNamesToExtract(dialog.exportableComponents))
90     StartupActionScriptManager.addActionCommand(StartupActionScriptManager.UnzipCommand(tempFile, File(configPath), filenameFilter))
91     // remove temp file
92     StartupActionScriptManager.addActionCommand(StartupActionScriptManager.DeleteCommand(tempFile))
93
94     UpdateSettings.getInstance().forceCheckForUpdateAfterRestart()
95
96     val key = if (ApplicationManager.getApplication().isRestartCapable)
97       "message.settings.imported.successfully.restart"
98     else
99       "message.settings.imported.successfully"
100     if (Messages.showOkCancelDialog(IdeBundle.message(key,
101       ApplicationNamesInfo.getInstance().productName,
102       ApplicationNamesInfo.getInstance().fullProductName),
103       IdeBundle.message("title.restart.needed"), Messages.getQuestionIcon()) == Messages.OK) {
104       (ApplicationManager.getApplication() as ApplicationEx).restart(true)
105     }
106   }
107
108   private fun getRelativeNamesToExtract(chosenComponents: Set<ExportableItem>): Set<String> {
109     val result = THashSet<String>()
110     val root = Paths.get(PathManager.getConfigPath())
111     for (chosenComponent in chosenComponents) {
112       for (exportFile in chosenComponent.files) {
113         result.add(root.relativize(exportFile).systemIndependentPath)
114       }
115     }
116
117     result.add(PluginManager.INSTALLED_TXT)
118     return result
119   }
120
121   private fun presentableFileName(file: File) = "'" + FileUtil.toSystemDependentName(file.path) + "'"
122
123   private fun promptLocationMessage() = IdeBundle.message("message.please.ensure.correct.settings")
124 }
125
126 fun getPaths(input: InputStream): Set<String> {
127   val result = THashSet<String>()
128   val zipIn = ZipInputStream(input)
129   try {
130     while (true) {
131       val entry = zipIn.nextEntry ?: break
132       var path = entry.name
133       result.add(path)
134       while (true) {
135         path = getParentPath(path) ?: break
136         result.add("$path/")
137       }
138     }
139   }
140   finally {
141     zipIn.close()
142   }
143   return result
144 }