github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+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  }