2019-06-08 08:35:43 +02:00
import { Config } from "data" ;
2019-06-19 21:24:05 +02:00
import Logger from "./logger" ;
2019-06-27 22:18:40 +02:00
import AddonManager from "./addonmanager" ;
import AddonError from "../structs/addonerror" ;
2019-06-08 08:35:43 +02:00
import Settings from "./settingsmanager" ;
2019-06-25 22:36:34 +02:00
import Strings from "./strings" ;
2019-06-23 06:11:50 +02:00
import Toasts from "../ui/toasts" ;
import Modals from "../ui/modals" ;
import SettingsRenderer from "../ui/settings" ;
2019-06-08 08:35:43 +02:00
const path = require ( "path" ) ;
const electronRemote = require ( "electron" ) . remote ;
2019-06-27 22:18:40 +02:00
export default new class PluginManager extends AddonManager {
2019-06-08 08:35:43 +02:00
get name ( ) { return "PluginManager" ; }
get moduleExtension ( ) { return ".js" ; }
get extension ( ) { return ".plugin.js" ; }
2019-06-27 22:18:40 +02:00
get addonFolder ( ) { return path . resolve ( Config . dataPath , "plugins" ) ; }
2019-06-08 08:35:43 +02:00
get prefix ( ) { return "plugin" ; }
2019-06-30 07:32:14 +02:00
get language ( ) { return "javascript" ; }
2019-06-08 08:35:43 +02:00
constructor ( ) {
super ( ) ;
this . onSwitch = this . onSwitch . bind ( this ) ;
this . observer = new MutationObserver ( ( mutations ) => {
for ( let i = 0 , mlen = mutations . length ; i < mlen ; i ++ ) {
this . onMutation ( mutations [ i ] ) ;
}
} ) ;
}
2019-05-28 20:19:48 +02:00
2019-06-24 21:47:24 +02:00
initialize ( ) {
const errors = super . initialize ( ) ;
this . setupFunctions ( ) ;
2019-06-27 22:18:40 +02:00
Settings . registerPanel ( "plugins" , Strings . Panels . plugins , { element : ( ) => SettingsRenderer . getAddonPanel ( Strings . Panels . plugins , this . addonList , this . state , {
folder : this . addonFolder ,
2019-06-24 21:47:24 +02:00
onChange : this . togglePlugin . bind ( this ) ,
reload : this . reloadPlugin . bind ( this ) ,
2019-06-30 07:32:14 +02:00
refreshList : this . updatePluginList . bind ( this ) ,
saveAddon : this . saveAddon . bind ( this ) ,
editAddon : this . editAddon . bind ( this ) ,
deleteAddon : this . deleteAddon . bind ( this ) ,
prefix : this . prefix
2019-06-24 21:47:24 +02:00
} ) } ) ;
return errors ;
}
2019-06-08 08:35:43 +02:00
/* Aliases */
updatePluginList ( ) { return this . updateList ( ) ; }
2019-06-27 22:18:40 +02:00
loadAllPlugins ( ) { return this . loadAllAddons ( ) ; }
2019-05-28 20:19:48 +02:00
2019-06-27 22:18:40 +02:00
enablePlugin ( idOrAddon ) { return this . enableAddon ( idOrAddon ) ; }
disablePlugin ( idOrAddon ) { return this . disableAddon ( idOrAddon ) ; }
togglePlugin ( id ) { return this . toggleAddon ( id ) ; }
2019-05-28 20:19:48 +02:00
2019-06-27 22:18:40 +02:00
unloadPlugin ( idOrFileOrAddon ) { return this . unloadAddon ( idOrFileOrAddon ) ; }
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
loadPlugin ( filename ) {
2019-06-27 22:18:40 +02:00
const error = this . loadAddon ( filename ) ;
if ( error ) Modals . showAddonErrors ( { themes : [ error ] } ) ;
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
2019-06-27 22:18:40 +02:00
reloadPlugin ( idOrFileOrAddon ) {
const error = this . reloadAddon ( idOrFileOrAddon ) ;
if ( error ) Modals . showAddonErrors ( { plugins : [ error ] } ) ;
return typeof ( idOrFileOrAddon ) == "string" ? this . addonList . find ( c => c . id == idOrFileOrAddon || c . filename == idOrFileOrAddon ) : idOrFileOrAddon ;
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
/* Overrides */
2019-06-27 22:18:40 +02:00
initializeAddon ( addon ) {
2020-07-29 21:06:54 +02:00
if ( ! addon . exports ) return new AddonError ( addon . name , addon . filename , "Plugin had no exports" , { message : "Plugin had no exports or no name property." , stack : "" } ) ;
2019-06-08 08:35:43 +02:00
try {
2020-07-29 21:06:54 +02:00
const PluginClass = addon . exports ;
2020-07-25 10:22:57 +02:00
const thePlugin = new PluginClass ( ) ;
2020-07-29 21:06:54 +02:00
addon . instance = thePlugin ;
2019-06-27 22:18:40 +02:00
addon . name = thePlugin . getName ( ) || addon . name ;
addon . author = thePlugin . getAuthor ( ) || addon . author || "No author" ;
addon . description = thePlugin . getDescription ( ) || addon . description || "No description" ;
addon . version = thePlugin . getVersion ( ) || addon . version || "No version" ;
2019-05-28 20:19:48 +02:00
try {
2020-07-29 21:06:54 +02:00
if ( typeof ( addon . instance . load ) == "function" ) addon . instance . load ( ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
catch ( error ) {
2019-06-27 22:18:40 +02:00
this . state [ addon . id ] = false ;
return new AddonError ( addon . name , addon . filename , "load() could not be fired." , { message : error . message , stack : error . stack } ) ;
2019-05-28 20:19:48 +02:00
}
}
2019-06-27 22:18:40 +02:00
catch ( error ) { return new AddonError ( addon . name , addon . filename , "Could not be constructed." , { message : error . message , stack : error . stack } ) ; }
2019-05-28 20:19:48 +02:00
}
2019-06-27 22:18:40 +02:00
getFileModification ( module , fileContent , meta ) {
2020-07-29 21:06:54 +02:00
fileContent += ` \n if (!module.exports || !module.exports.prototype || !module.exports.prototype.start) {module.exports = ${ meta . exports || meta . name } ;} ` ;
2019-06-27 22:18:40 +02:00
module . _compile ( fileContent , module . filename ) ;
2020-07-29 21:06:54 +02:00
meta . exports = module . exports ;
module . exports = meta ;
return "" ;
2019-05-28 20:19:48 +02:00
}
2019-06-27 22:18:40 +02:00
startAddon ( id ) { return this . startPlugin ( id ) ; }
stopAddon ( id ) { return this . stopPlugin ( id ) ; }
2020-07-19 01:01:49 +02:00
getAddon ( id ) { return this . getPlugin ( id ) ; }
2019-06-08 08:35:43 +02:00
2019-06-27 22:18:40 +02:00
startPlugin ( idOrAddon ) {
const addon = typeof ( idOrAddon ) == "string" ? this . addonList . find ( p => p . id == idOrAddon ) : idOrAddon ;
if ( ! addon ) return ;
2020-07-29 21:06:54 +02:00
const plugin = addon . instance ;
2019-06-08 08:35:43 +02:00
try {
plugin . start ( ) ;
}
catch ( err ) {
2019-06-27 22:18:40 +02:00
this . state [ addon . id ] = false ;
2020-07-19 01:01:49 +02:00
Toasts . error ( Strings . Addons . couldNotStart . format ( { name : addon . name , version : addon . version } ) ) ;
2019-06-27 22:18:40 +02:00
Logger . stacktrace ( this . name , addon . name + " could not be started." , err ) ;
2020-07-19 01:01:49 +02:00
return new AddonError ( addon . name , addon . filename , Strings . Addons . enabled . format ( { method : "start()" } ) , { message : err . message , stack : err . stack } ) ;
2019-06-08 08:35:43 +02:00
}
2020-07-19 01:01:49 +02:00
this . emit ( "started" , addon . id ) ;
Toasts . show ( Strings . Addons . enabled . format ( { name : addon . name , version : addon . version } ) ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
2019-06-27 22:18:40 +02:00
stopPlugin ( idOrAddon ) {
const addon = typeof ( idOrAddon ) == "string" ? this . addonList . find ( p => p . id == idOrAddon ) : idOrAddon ;
if ( ! addon ) return ;
2020-07-29 21:06:54 +02:00
const plugin = addon . instance ;
2019-06-08 08:35:43 +02:00
try {
plugin . stop ( ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
catch ( err ) {
2019-06-27 22:18:40 +02:00
this . state [ addon . id ] = false ;
2020-07-19 01:01:49 +02:00
Toasts . error ( Strings . Addons . couldNotStop . format ( { name : addon . name , version : addon . version } ) ) ;
2019-06-27 22:18:40 +02:00
Logger . stacktrace ( this . name , addon . name + " could not be stopped." , err ) ;
2020-07-19 01:01:49 +02:00
return new AddonError ( addon . name , addon . filename , Strings . Addons . enabled . format ( { method : "stop()" } ) , { message : err . message , stack : err . stack } ) ;
2019-06-08 08:35:43 +02:00
}
2020-07-19 01:01:49 +02:00
this . emit ( "stopped" , addon . id ) ;
Toasts . show ( Strings . Addons . disabled . format ( { name : addon . name , version : addon . version } ) ) ;
}
getPlugin ( idOrFile ) {
const addon = this . addonList . find ( c => c . id == idOrFile || c . filename == idOrFile ) ;
if ( ! addon ) return ;
2020-07-29 21:06:54 +02:00
return addon . instance ;
2019-06-08 08:35:43 +02:00
}
setupFunctions ( ) {
electronRemote . getCurrentWebContents ( ) . on ( "did-navigate-in-page" , this . onSwitch . bind ( this ) ) ;
this . observer . observe ( document , {
childList : true ,
subtree : true
} ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
onSwitch ( ) {
this . emit ( "page-switch" ) ;
2019-06-27 22:18:40 +02:00
for ( let i = 0 ; i < this . addonList . length ; i ++ ) {
2020-07-29 21:06:54 +02:00
const plugin = this . addonList [ i ] . instance ;
2019-06-27 22:18:40 +02:00
if ( ! this . state [ this . addonList [ i ] . id ] ) continue ;
2019-06-08 08:35:43 +02:00
if ( typeof ( plugin . onSwitch ) === "function" ) {
2020-07-25 10:22:57 +02:00
try { plugin . onSwitch ( ) ; }
catch ( err ) { Logger . stacktrace ( this . name , "Unable to fire onSwitch for " + this . addonList [ i ] . name + "." , err ) ; }
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
onMutation ( mutation ) {
2019-06-27 22:18:40 +02:00
for ( let i = 0 ; i < this . addonList . length ; i ++ ) {
2020-07-29 21:06:54 +02:00
const plugin = this . addonList [ i ] . instance ;
2019-06-27 22:18:40 +02:00
if ( ! this . state [ this . addonList [ i ] . id ] ) continue ;
2019-06-08 08:35:43 +02:00
if ( typeof plugin . observer === "function" ) {
2020-07-25 10:22:57 +02:00
try { plugin . observer ( mutation ) ; }
catch ( err ) { Logger . stacktrace ( this . name , "Unable to fire observer for " + this . addonList [ i ] . name + "." , err ) ; }
2019-06-08 08:35:43 +02:00
}
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
} ;