2018-01-30 16:59:27 +01:00
/ * *
* BetterDiscord Content Manager Module
* Copyright ( c ) 2015 - present Jiiks / JsSucks - https : //github.com/Jiiks / https://github.com/JsSucks
* All rights reserved .
* https : //betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
* /
import Globals from './globals' ;
import { FileUtils , ClientLogger as Logger } from 'common' ;
import path from 'path' ;
2018-02-07 13:15:46 +01:00
import { Events } from 'modules' ;
2018-02-07 15:41:10 +01:00
import { Error } from 'structs' ;
2018-01-30 16:59:27 +01:00
export default class {
2018-02-07 17:02:27 +01:00
static get errors ( ) {
return this . _errors || ( this . _errors = [ ] ) ;
}
2018-01-30 16:59:27 +01:00
static get localContent ( ) {
return this . _localContent ? this . _localContent : ( this . _localContent = [ ] ) ;
}
static get contentPath ( ) {
return this . _contentPath ? this . _contentPath : ( this . _contentPath = Globals . getObject ( 'paths' ) . find ( path => path . id === this . pathId ) . path ) ;
}
static async loadAllContent ( ) {
try {
2018-01-30 23:21:06 +01:00
await FileUtils . ensureDirectory ( this . contentPath ) ;
const directories = await FileUtils . listDirectory ( this . contentPath ) ;
for ( let dir of directories ) {
try {
await this . preloadContent ( dir ) ;
} catch ( err ) {
2018-02-07 17:02:27 +01:00
this . errors . push ( new Error ( {
module : this . moduleName ,
message : ` Failed to load ${ dir } ` ,
err
} ) ) ;
2018-02-11 20:31:24 +01:00
2018-01-30 23:21:06 +01:00
Logger . err ( this . moduleName , err ) ;
}
}
2018-02-07 17:02:27 +01:00
if ( this . errors . length ) {
Events . emit ( 'bd-error' , {
header : ` ${ this . moduleName } - one or more ${ this . contentType } (s) failed to load ` ,
module : this . moduleName ,
type : 'err' ,
content : this . errors
} ) ;
}
this . _errors = [ ] ;
2018-01-30 23:21:06 +01:00
return this . localContent ;
} catch ( err ) {
throw err ;
}
}
2018-01-30 16:59:27 +01:00
2018-01-30 23:21:06 +01:00
static async refreshContent ( ) {
if ( ! this . localContent . length ) return this . loadAllContent ( ) ;
try {
2018-01-30 16:59:27 +01:00
await FileUtils . ensureDirectory ( this . contentPath ) ;
const directories = await FileUtils . listDirectory ( this . contentPath ) ;
for ( let dir of directories ) {
2018-01-30 23:21:06 +01:00
// If content is already loaded this should resolve.
if ( this . getContentByDirName ( dir ) ) continue ;
2018-01-30 16:59:27 +01:00
try {
2018-01-30 23:21:06 +01:00
// Load if not
2018-01-30 16:59:27 +01:00
await this . preloadContent ( dir ) ;
} catch ( err ) {
//We don't want every plugin/theme to fail loading when one does
Logger . err ( this . moduleName , err ) ;
}
}
2018-01-30 23:21:06 +01:00
for ( let content of this . localContent ) {
if ( directories . includes ( content . dirName ) ) continue ;
//Plugin/theme was deleted manually, stop it and remove any reference
this . unloadContent ( content ) ;
}
2018-01-30 16:59:27 +01:00
return this . localContent ;
2018-01-30 23:21:06 +01:00
2018-01-30 16:59:27 +01:00
} catch ( err ) {
throw err ;
}
}
static async preloadContent ( dirName , reload = false , index ) {
try {
const contentPath = path . join ( this . contentPath , dirName ) ;
await FileUtils . directoryExists ( contentPath ) ;
if ( ! reload ) {
const loaded = this . localContent . find ( content => content . contentPath === contentPath ) ;
if ( loaded ) {
throw { 'message' : ` Attempted to load already loaded user content: ${ path } ` } ;
}
}
const readConfig = await this . readConfig ( contentPath ) ;
const mainPath = path . join ( contentPath , readConfig . main ) ;
const userConfig = {
enabled : false ,
config : readConfig . defaultConfig
} ;
try {
const readUserConfig = await this . readUserConfig ( contentPath ) ;
2018-02-03 10:25:34 +01:00
userConfig . enabled = readUserConfig . enabled || false ;
userConfig . config = readConfig . defaultConfig . map ( config => {
2018-02-04 21:17:22 +01:00
const userSet = readUserConfig . config . find ( c => c . category === config . category ) ;
// return userSet || config;
if ( ! userSet ) return config ;
config . settings = config . settings . map ( setting => {
const userSetting = userSet . settings . find ( s => s . id === setting . id ) ;
if ( ! userSetting ) return setting ;
setting . value = userSetting . value ;
return setting ;
} ) ;
return config ;
2018-01-30 16:59:27 +01:00
} ) ;
2018-02-11 20:31:24 +01:00
userConfig . css = readUserConfig . css || null ;
2018-02-04 21:17:22 +01:00
// userConfig.config = readUserConfig.config;
2018-02-03 10:25:34 +01:00
} catch ( err ) { /*We don't care if this fails it either means that user config doesn't exist or there's something wrong with it so we revert to default config*/
2018-02-04 21:17:22 +01:00
2018-02-03 10:25:34 +01:00
}
2018-01-30 16:59:27 +01:00
const configs = {
defaultConfig : readConfig . defaultConfig ,
userConfig
}
const paths = {
contentPath ,
dirName ,
mainPath
}
const content = await this . loadContent ( paths , configs , readConfig . info , readConfig . main ) ;
2018-01-31 09:17:15 +01:00
if ( reload ) this . localContent [ index ] = content ;
else this . localContent . push ( content ) ;
2018-01-30 16:59:27 +01:00
return content ;
} catch ( err ) {
throw err ;
}
}
2018-02-04 21:17:22 +01:00
2018-01-30 16:59:27 +01:00
static async readConfig ( configPath ) {
configPath = path . resolve ( configPath , 'config.json' ) ;
return FileUtils . readJsonFromFile ( configPath ) ;
}
static async readUserConfig ( configPath ) {
configPath = path . resolve ( configPath , 'user.config.json' ) ;
return FileUtils . readJsonFromFile ( configPath ) ;
}
2018-01-30 23:21:06 +01:00
//TODO make this nicer
static findContent ( wild ) {
let content = this . getContentByName ( wild ) ;
if ( content ) return content ;
content = this . getContentById ( wild ) ;
if ( content ) return content ;
content = this . getContentByPath ( wild ) ;
if ( content ) return content ;
return this . getContentByDirName ( wild ) ;
}
static getContentIndex ( content ) { return this . localContent . findIndex ( c => c === content ) }
static getContentByName ( name ) { return this . localContent . find ( c => c . name === name ) }
static getContentById ( id ) { return this . localContent . find ( c => c . id === id ) }
static getContentByPath ( path ) { return this . localContent . find ( c => c . contentPath === path ) }
static getContentByDirName ( dirName ) { return this . localContent . find ( c => c . dirName === dirName ) }
2018-02-05 15:33:30 +01:00
}