mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Merge 833e858394 into 39fd8a6550
This commit is contained in:
commit
c602de5b78
10 changed files with 66 additions and 11 deletions
|
|
@ -394,6 +394,9 @@ input ConfigInterfaceInput {
|
||||||
customLocales: String
|
customLocales: String
|
||||||
customLocalesEnabled: Boolean
|
customLocalesEnabled: Boolean
|
||||||
|
|
||||||
|
"When true, disables all customizations (plugins, CSS, JavaScript, locales) for troubleshooting"
|
||||||
|
disableCustomizations: Boolean
|
||||||
|
|
||||||
"Interface language"
|
"Interface language"
|
||||||
language: String
|
language: String
|
||||||
|
|
||||||
|
|
@ -467,6 +470,9 @@ type ConfigInterfaceResult {
|
||||||
customLocales: String
|
customLocales: String
|
||||||
customLocalesEnabled: Boolean
|
customLocalesEnabled: Boolean
|
||||||
|
|
||||||
|
"When true, disables all customizations (plugins, CSS, JavaScript, locales) for troubleshooting"
|
||||||
|
disableCustomizations: Boolean
|
||||||
|
|
||||||
"Interface language"
|
"Interface language"
|
||||||
language: String
|
language: String
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -515,6 +515,8 @@ func (r *mutationResolver) ConfigureInterface(ctx context.Context, input ConfigI
|
||||||
|
|
||||||
r.setConfigBool(config.CustomLocalesEnabled, input.CustomLocalesEnabled)
|
r.setConfigBool(config.CustomLocalesEnabled, input.CustomLocalesEnabled)
|
||||||
|
|
||||||
|
r.setConfigBool(config.DisableCustomizations, input.DisableCustomizations)
|
||||||
|
|
||||||
if input.DisableDropdownCreate != nil {
|
if input.DisableDropdownCreate != nil {
|
||||||
ddc := input.DisableDropdownCreate
|
ddc := input.DisableDropdownCreate
|
||||||
r.setConfigBool(config.DisableDropdownCreatePerformer, ddc.Performer)
|
r.setConfigBool(config.DisableDropdownCreatePerformer, ddc.Performer)
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ func makeConfigInterfaceResult() *ConfigInterfaceResult {
|
||||||
javascriptEnabled := config.GetJavascriptEnabled()
|
javascriptEnabled := config.GetJavascriptEnabled()
|
||||||
customLocales := config.GetCustomLocales()
|
customLocales := config.GetCustomLocales()
|
||||||
customLocalesEnabled := config.GetCustomLocalesEnabled()
|
customLocalesEnabled := config.GetCustomLocalesEnabled()
|
||||||
|
disableCustomizations := config.GetDisableCustomizations()
|
||||||
language := config.GetLanguage()
|
language := config.GetLanguage()
|
||||||
handyKey := config.GetHandyKey()
|
handyKey := config.GetHandyKey()
|
||||||
scriptOffset := config.GetFunscriptOffset()
|
scriptOffset := config.GetFunscriptOffset()
|
||||||
|
|
@ -183,6 +184,7 @@ func makeConfigInterfaceResult() *ConfigInterfaceResult {
|
||||||
JavascriptEnabled: &javascriptEnabled,
|
JavascriptEnabled: &javascriptEnabled,
|
||||||
CustomLocales: &customLocales,
|
CustomLocales: &customLocales,
|
||||||
CustomLocalesEnabled: &customLocalesEnabled,
|
CustomLocalesEnabled: &customLocalesEnabled,
|
||||||
|
DisableCustomizations: &disableCustomizations,
|
||||||
Language: &language,
|
Language: &language,
|
||||||
|
|
||||||
ImageLightbox: &imageLightboxOptions,
|
ImageLightbox: &imageLightboxOptions,
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ func cssHandler(c *config.Config) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var paths []string
|
var paths []string
|
||||||
|
|
||||||
if c.GetCSSEnabled() {
|
if c.GetCSSEnabled() && !c.GetDisableCustomizations() {
|
||||||
// search for custom.css in current directory, then $HOME/.stash
|
// search for custom.css in current directory, then $HOME/.stash
|
||||||
fn := c.GetCSSPath()
|
fn := c.GetCSSPath()
|
||||||
exists, _ := fsutil.FileExists(fn)
|
exists, _ := fsutil.FileExists(fn)
|
||||||
|
|
@ -439,7 +439,7 @@ func javascriptHandler(c *config.Config) func(w http.ResponseWriter, r *http.Req
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var paths []string
|
var paths []string
|
||||||
|
|
||||||
if c.GetJavascriptEnabled() {
|
if c.GetJavascriptEnabled() && !c.GetDisableCustomizations() {
|
||||||
// search for custom.js in current directory, then $HOME/.stash
|
// search for custom.js in current directory, then $HOME/.stash
|
||||||
fn := c.GetJavascriptPath()
|
fn := c.GetJavascriptPath()
|
||||||
exists, _ := fsutil.FileExists(fn)
|
exists, _ := fsutil.FileExists(fn)
|
||||||
|
|
@ -457,7 +457,7 @@ func customLocalesHandler(c *config.Config) func(w http.ResponseWriter, r *http.
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
|
|
||||||
if c.GetCustomLocalesEnabled() {
|
if c.GetCustomLocalesEnabled() && !c.GetDisableCustomizations() {
|
||||||
// search for custom-locales.json in current directory, then $HOME/.stash
|
// search for custom-locales.json in current directory, then $HOME/.stash
|
||||||
path := c.GetCustomLocalesPath()
|
path := c.GetCustomLocalesPath()
|
||||||
exists, _ := fsutil.FileExists(path)
|
exists, _ := fsutil.FileExists(path)
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,7 @@ const (
|
||||||
CSSEnabled = "cssenabled"
|
CSSEnabled = "cssenabled"
|
||||||
JavascriptEnabled = "javascriptenabled"
|
JavascriptEnabled = "javascriptenabled"
|
||||||
CustomLocalesEnabled = "customlocalesenabled"
|
CustomLocalesEnabled = "customlocalesenabled"
|
||||||
|
DisableCustomizations = "disable_customizations"
|
||||||
|
|
||||||
ShowScrubber = "show_scrubber"
|
ShowScrubber = "show_scrubber"
|
||||||
showScrubberDefault = true
|
showScrubberDefault = true
|
||||||
|
|
@ -1457,6 +1458,13 @@ func (i *Config) GetCustomLocalesEnabled() bool {
|
||||||
return i.getBool(CustomLocalesEnabled)
|
return i.getBool(CustomLocalesEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDisableCustomizations returns true if all customizations (plugins, custom CSS,
|
||||||
|
// custom JavaScript, and custom locales) should be disabled. This is useful for
|
||||||
|
// troubleshooting issues without permanently disabling individual customizations.
|
||||||
|
func (i *Config) GetDisableCustomizations() bool {
|
||||||
|
return i.getBool(DisableCustomizations)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Config) GetHandyKey() string {
|
func (i *Config) GetHandyKey() string {
|
||||||
return i.getString(HandyKey)
|
return i.getString(HandyKey)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ fragment ConfigInterfaceData on ConfigInterfaceResult {
|
||||||
javascriptEnabled
|
javascriptEnabled
|
||||||
customLocales
|
customLocales
|
||||||
customLocalesEnabled
|
customLocalesEnabled
|
||||||
|
disableCustomizations
|
||||||
language
|
language
|
||||||
imageLightbox {
|
imageLightbox {
|
||||||
slideshowDelay
|
slideshowDelay
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,12 @@ export const App: React.FC = () => {
|
||||||
formats={intlFormats}
|
formats={intlFormats}
|
||||||
>
|
>
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<PluginsLoader>
|
<PluginsLoader
|
||||||
|
disableCustomizations={
|
||||||
|
config.data?.configuration?.interface?.disableCustomizations ??
|
||||||
|
false
|
||||||
|
}
|
||||||
|
>
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
<ConfigurationProvider configuration={config.data!.configuration}>
|
<ConfigurationProvider configuration={config.data!.configuration}>
|
||||||
{maybeRenderReleaseNotes()}
|
{maybeRenderReleaseNotes()}
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,16 @@ export const SettingsInterfacePanel: React.FC = PatchComponent(
|
||||||
/>
|
/>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
|
|
||||||
|
<SettingSection headingID="config.ui.troubleshooting.heading">
|
||||||
|
<BooleanSetting
|
||||||
|
id="disable-customizations"
|
||||||
|
headingID="config.ui.troubleshooting.disable_customizations.heading"
|
||||||
|
subHeadingID="config.ui.troubleshooting.disable_customizations.description"
|
||||||
|
checked={iface.disableCustomizations ?? undefined}
|
||||||
|
onChange={(v) => saveInterface({ disableCustomizations: v })}
|
||||||
|
/>
|
||||||
|
</SettingSection>
|
||||||
|
|
||||||
<SettingSection headingID="config.ui.scene_wall.heading">
|
<SettingSection headingID="config.ui.scene_wall.heading">
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
id="wall-show-title"
|
id="wall-show-title"
|
||||||
|
|
|
||||||
|
|
@ -611,6 +611,13 @@
|
||||||
"heading": "Custom CSS",
|
"heading": "Custom CSS",
|
||||||
"option_label": "Custom CSS enabled"
|
"option_label": "Custom CSS enabled"
|
||||||
},
|
},
|
||||||
|
"troubleshooting": {
|
||||||
|
"heading": "Troubleshooting",
|
||||||
|
"disable_customizations": {
|
||||||
|
"heading": "Disable customizations",
|
||||||
|
"description": "Temporarily disables all plugins, custom CSS, custom JavaScript, and custom locales. Use this to troubleshoot issues without permanently disabling individual customizations. A page reload is required for changes to take effect."
|
||||||
|
}
|
||||||
|
},
|
||||||
"custom_javascript": {
|
"custom_javascript": {
|
||||||
"description": "Page must be reloaded for changes to take effect. There is no guarantee of compatibility between custom Javascript and future releases of Stash.",
|
"description": "Page must be reloaded for changes to take effect. There is no guarantee of compatibility between custom Javascript and future releases of Stash.",
|
||||||
"heading": "Custom Javascript",
|
"heading": "Custom Javascript",
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,8 @@ function sortPlugins(plugins: PluginList) {
|
||||||
|
|
||||||
// load all plugins and their dependencies
|
// load all plugins and their dependencies
|
||||||
// returns true when all plugins are loaded, regardess of success or failure
|
// returns true when all plugins are loaded, regardess of success or failure
|
||||||
function useLoadPlugins() {
|
// if disableCustomizations is true, skip loading plugins entirely
|
||||||
|
function useLoadPlugins(disableCustomizations?: boolean) {
|
||||||
const {
|
const {
|
||||||
data: plugins,
|
data: plugins,
|
||||||
loading: pluginsLoading,
|
loading: pluginsLoading,
|
||||||
|
|
@ -74,6 +75,12 @@ function useLoadPlugins() {
|
||||||
}, [plugins?.plugins, pluginsLoading, pluginsError]);
|
}, [plugins?.plugins, pluginsLoading, pluginsError]);
|
||||||
|
|
||||||
const pluginJavascripts = useMemoOnce(() => {
|
const pluginJavascripts = useMemoOnce(() => {
|
||||||
|
// Skip loading plugin JS if customizations are disabled.
|
||||||
|
// Note: We check inside useMemoOnce rather than early-returning from useLoadPlugins
|
||||||
|
// to comply with React's rules of hooks - hooks must be called unconditionally.
|
||||||
|
if (disableCustomizations) {
|
||||||
|
return [[], true];
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
uniq(
|
uniq(
|
||||||
sortedPlugins
|
sortedPlugins
|
||||||
|
|
@ -83,9 +90,12 @@ function useLoadPlugins() {
|
||||||
),
|
),
|
||||||
!!sortedPlugins && !pluginsLoading && !pluginsError,
|
!!sortedPlugins && !pluginsLoading && !pluginsError,
|
||||||
];
|
];
|
||||||
}, [sortedPlugins, pluginsLoading, pluginsError]);
|
}, [sortedPlugins, pluginsLoading, pluginsError, disableCustomizations]);
|
||||||
|
|
||||||
const pluginCSS = useMemoOnce(() => {
|
const pluginCSS = useMemoOnce(() => {
|
||||||
|
if (disableCustomizations) {
|
||||||
|
return [[], true];
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
uniq(
|
uniq(
|
||||||
sortedPlugins
|
sortedPlugins
|
||||||
|
|
@ -95,7 +105,7 @@ function useLoadPlugins() {
|
||||||
),
|
),
|
||||||
!!sortedPlugins && !pluginsLoading && !pluginsError,
|
!!sortedPlugins && !pluginsLoading && !pluginsError,
|
||||||
];
|
];
|
||||||
}, [sortedPlugins, pluginsLoading, pluginsError]);
|
}, [sortedPlugins, pluginsLoading, pluginsError, disableCustomizations]);
|
||||||
|
|
||||||
const pluginJavascriptLoaded = useScript(
|
const pluginJavascriptLoaded = useScript(
|
||||||
pluginJavascripts ?? [],
|
pluginJavascripts ?? [],
|
||||||
|
|
@ -109,11 +119,15 @@ function useLoadPlugins() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PluginsLoader: React.FC<React.PropsWithChildren<{}>> = ({
|
interface IPluginsLoaderProps {
|
||||||
children,
|
disableCustomizations?: boolean;
|
||||||
}) => {
|
}
|
||||||
|
|
||||||
|
export const PluginsLoader: React.FC<
|
||||||
|
React.PropsWithChildren<IPluginsLoaderProps>
|
||||||
|
> = ({ disableCustomizations, children }) => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
const { loading: loaded, error } = useLoadPlugins();
|
const { loading: loaded, error } = useLoadPlugins(disableCustomizations);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue