github.com/jlevesy/mattermost-server@v5.3.2-0.20181003190404-7468f35cb0c8+incompatible/app/plugin.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "net/http" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/mattermost/mattermost-server/mlog" 13 "github.com/mattermost/mattermost-server/model" 14 "github.com/mattermost/mattermost-server/plugin" 15 "github.com/mattermost/mattermost-server/utils" 16 ) 17 18 func (a *App) SyncPluginsActiveState() { 19 if a.Plugins == nil { 20 return 21 } 22 23 config := a.Config().PluginSettings 24 25 if *config.Enable { 26 availablePlugins, err := a.Plugins.Available() 27 if err != nil { 28 a.Log.Error("Unable to get available plugins", mlog.Err(err)) 29 return 30 } 31 32 // Deactivate any plugins that have been disabled. 33 for _, plugin := range a.Plugins.Active() { 34 // Determine if plugin is enabled 35 pluginId := plugin.Manifest.Id 36 pluginEnabled := false 37 if state, ok := config.PluginStates[pluginId]; ok { 38 pluginEnabled = state.Enable 39 } 40 41 // If it's not enabled we need to deactivate it 42 if !pluginEnabled { 43 deactivated := a.Plugins.Deactivate(pluginId) 44 if deactivated && plugin.Manifest.HasClient() { 45 message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_DISABLED, "", "", "", nil) 46 message.Add("manifest", plugin.Manifest.ClientManifest()) 47 a.Publish(message) 48 } 49 } 50 } 51 52 // Activate any plugins that have been enabled 53 for _, plugin := range availablePlugins { 54 if plugin.Manifest == nil { 55 plugin.WrapLogger(a.Log).Error("Plugin manifest could not be loaded", mlog.Err(plugin.ManifestError)) 56 continue 57 } 58 59 // Determine if plugin is enabled 60 pluginId := plugin.Manifest.Id 61 pluginEnabled := false 62 if state, ok := config.PluginStates[pluginId]; ok { 63 pluginEnabled = state.Enable 64 } 65 66 // Activate plugin if enabled 67 if pluginEnabled { 68 updatedManifest, activated, err := a.Plugins.Activate(pluginId) 69 if err != nil { 70 plugin.WrapLogger(a.Log).Error("Unable to activate plugin", mlog.Err(err)) 71 continue 72 } 73 74 if activated && updatedManifest.HasClient() { 75 message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_ENABLED, "", "", "", nil) 76 message.Add("manifest", updatedManifest.ClientManifest()) 77 a.Publish(message) 78 } 79 } 80 } 81 } else { // If plugins are disabled, shutdown plugins. 82 a.Plugins.Shutdown() 83 } 84 85 if err := a.notifyPluginStatusesChanged(); err != nil { 86 mlog.Error("failed to notify plugin status changed", mlog.Err(err)) 87 } 88 } 89 90 func (a *App) NewPluginAPI(manifest *model.Manifest) plugin.API { 91 return NewPluginAPI(a, manifest) 92 } 93 94 func (a *App) InitPlugins(pluginDir, webappPluginDir string) { 95 if a.Plugins != nil || !*a.Config().PluginSettings.Enable { 96 a.SyncPluginsActiveState() 97 return 98 } 99 100 a.Log.Info("Starting up plugins") 101 102 if err := os.Mkdir(pluginDir, 0744); err != nil && !os.IsExist(err) { 103 mlog.Error("Failed to start up plugins", mlog.Err(err)) 104 return 105 } 106 107 if err := os.Mkdir(webappPluginDir, 0744); err != nil && !os.IsExist(err) { 108 mlog.Error("Failed to start up plugins", mlog.Err(err)) 109 return 110 } 111 112 if env, err := plugin.NewEnvironment(a.NewPluginAPI, pluginDir, webappPluginDir, a.Log); err != nil { 113 mlog.Error("Failed to start up plugins", mlog.Err(err)) 114 return 115 } else { 116 a.Plugins = env 117 } 118 119 prepackagedPluginsDir, found := utils.FindDir("prepackaged_plugins") 120 if found { 121 if err := filepath.Walk(prepackagedPluginsDir, func(walkPath string, info os.FileInfo, err error) error { 122 if !strings.HasSuffix(walkPath, ".tar.gz") { 123 return nil 124 } 125 126 if fileReader, err := os.Open(walkPath); err != nil { 127 mlog.Error("Failed to open prepackaged plugin", mlog.Err(err), mlog.String("path", walkPath)) 128 } else if _, err := a.InstallPlugin(fileReader, true); err != nil { 129 mlog.Error("Failed to unpack prepackaged plugin", mlog.Err(err), mlog.String("path", walkPath)) 130 } 131 132 return nil 133 }); err != nil { 134 mlog.Error("Failed to complete unpacking prepackaged plugins", mlog.Err(err)) 135 } 136 } 137 138 // Sync plugin active state when config changes. Also notify plugins. 139 a.RemoveConfigListener(a.PluginConfigListenerId) 140 a.PluginConfigListenerId = a.AddConfigListener(func(*model.Config, *model.Config) { 141 a.SyncPluginsActiveState() 142 a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool { 143 hooks.OnConfigurationChange() 144 return true 145 }, plugin.OnConfigurationChangeId) 146 }) 147 148 a.SyncPluginsActiveState() 149 } 150 151 func (a *App) ShutDownPlugins() { 152 if a.Plugins == nil { 153 return 154 } 155 156 mlog.Info("Shutting down plugins") 157 158 a.Plugins.Shutdown() 159 160 a.RemoveConfigListener(a.PluginConfigListenerId) 161 a.PluginConfigListenerId = "" 162 a.Plugins = nil 163 } 164 165 func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) { 166 if a.Plugins == nil || !*a.Config().PluginSettings.Enable { 167 return nil, model.NewAppError("GetActivePluginManifests", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) 168 } 169 170 plugins := a.Plugins.Active() 171 172 manifests := make([]*model.Manifest, len(plugins)) 173 for i, plugin := range plugins { 174 manifests[i] = plugin.Manifest 175 } 176 177 return manifests, nil 178 } 179 180 // EnablePlugin will set the config for an installed plugin to enabled, triggering asynchronous 181 // activation if inactive anywhere in the cluster. 182 func (a *App) EnablePlugin(id string) *model.AppError { 183 if a.Plugins == nil || !*a.Config().PluginSettings.Enable { 184 return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) 185 } 186 187 plugins, err := a.Plugins.Available() 188 if err != nil { 189 return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError) 190 } 191 192 id = strings.ToLower(id) 193 194 var manifest *model.Manifest 195 for _, p := range plugins { 196 if p.Manifest.Id == id { 197 manifest = p.Manifest 198 break 199 } 200 } 201 202 if manifest == nil { 203 return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusBadRequest) 204 } 205 206 a.UpdateConfig(func(cfg *model.Config) { 207 cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true} 208 }) 209 210 // This call will cause SyncPluginsActiveState to be called and the plugin to be activated 211 if err := a.SaveConfig(a.Config(), true); err != nil { 212 if err.Id == "ent.cluster.save_config.error" { 213 return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError) 214 } 215 return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError) 216 } 217 218 return nil 219 } 220 221 // DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active. 222 func (a *App) DisablePlugin(id string) *model.AppError { 223 if a.Plugins == nil || !*a.Config().PluginSettings.Enable { 224 return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) 225 } 226 227 plugins, err := a.Plugins.Available() 228 if err != nil { 229 return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError) 230 } 231 232 id = strings.ToLower(id) 233 234 var manifest *model.Manifest 235 for _, p := range plugins { 236 if p.Manifest.Id == id { 237 manifest = p.Manifest 238 break 239 } 240 } 241 242 if manifest == nil { 243 return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusBadRequest) 244 } 245 246 a.UpdateConfig(func(cfg *model.Config) { 247 cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false} 248 }) 249 250 if err := a.SaveConfig(a.Config(), true); err != nil { 251 return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError) 252 } 253 254 return nil 255 } 256 257 func (a *App) PluginsReady() bool { 258 return a.Plugins != nil && *a.Config().PluginSettings.Enable 259 } 260 261 func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) { 262 if !a.PluginsReady() { 263 return nil, model.NewAppError("GetPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) 264 } 265 266 availablePlugins, err := a.Plugins.Available() 267 if err != nil { 268 return nil, model.NewAppError("GetPlugins", "app.plugin.get_plugins.app_error", nil, err.Error(), http.StatusInternalServerError) 269 } 270 resp := &model.PluginsResponse{Active: []*model.PluginInfo{}, Inactive: []*model.PluginInfo{}} 271 for _, plugin := range availablePlugins { 272 if plugin.Manifest == nil { 273 continue 274 } 275 276 info := &model.PluginInfo{ 277 Manifest: *plugin.Manifest, 278 } 279 280 if a.Plugins.IsActive(plugin.Manifest.Id) { 281 resp.Active = append(resp.Active, info) 282 } else { 283 resp.Inactive = append(resp.Inactive, info) 284 } 285 } 286 287 return resp, nil 288 }