Verified Commit 2989628b authored by Chocobozzz's avatar Chocobozzz
Browse files

Use HTML config when possible

parent 8e08d415
......@@ -22,10 +22,10 @@ import { FormReactiveValidationMessages, FormValidatorService } from '@app/share
import { InstanceService } from '@app/shared/shared-instance'
import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main'
import {
HTMLServerConfig,
LiveVideo,
RegisterClientFormFieldOptions,
RegisterClientVideoFieldOptions,
ServerConfig,
VideoConstant,
VideoDetails,
VideoPrivacy
......@@ -84,7 +84,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
calendarTimezone: string
calendarDateFormat: string
serverConfig: ServerConfig
serverConfig: HTMLServerConfig
pluginFields: PluginField[] = []
......@@ -194,9 +194,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
}
})
this.serverConfig = this.serverService.getTmpConfig()
this.serverService.getConfig()
.subscribe(config => this.serverConfig = config)
this.serverConfig = this.serverService.getHTMLConfig()
this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
......
......@@ -6,7 +6,7 @@ import { listUserChannels } from '@app/helpers'
import { FormReactive } from '@app/shared/shared-forms'
import { VideoCaptionEdit, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
import { HTMLServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
@Directive()
// tslint:disable-next-line: directive-class-suffix
......@@ -28,7 +28,7 @@ export abstract class VideoSend extends FormReactive implements OnInit {
protected serverService: ServerService
protected videoService: VideoService
protected videoCaptionService: VideoCaptionService
protected serverConfig: ServerConfig
protected serverConfig: HTMLServerConfig
abstract canDeactivate (): CanComponentDeactivateResult
......@@ -41,9 +41,7 @@ export abstract class VideoSend extends FormReactive implements OnInit {
this.firstStepChannelId = this.userVideoChannels[0].id
})
this.serverConfig = this.serverService.getTmpConfig()
this.serverService.getConfig()
.subscribe(config => this.serverConfig = config)
this.serverConfig = this.serverService.getHTMLConfig()
this.serverService.getVideoPrivacies()
.subscribe(
......
import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core'
import { ServerConfig } from '@shared/models'
import { HTMLServerConfig } from '@shared/models'
import { VideoEditType } from './shared/video-edit.type'
import { VideoGoLiveComponent } from './video-add-components/video-go-live.component'
import { VideoImportTorrentComponent } from './video-add-components/video-import-torrent.component'
......@@ -26,7 +26,7 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
activeNav: string
private serverConfig: ServerConfig
private serverConfig: HTMLServerConfig
constructor (
private auth: AuthService,
......@@ -42,10 +42,7 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate {
ngOnInit () {
this.user = this.auth.getUser()
this.serverConfig = this.serverService.getTmpConfig()
this.serverService.getConfig()
.subscribe(config => this.serverConfig = config)
this.serverConfig = this.serverService.getHTMLConfig()
this.user = this.auth.getUser()
......
......@@ -4,7 +4,7 @@ import { Injectable } from '@angular/core'
import { ServerService, UserService } from '@app/core'
import { Video, VideoService } from '@app/shared/shared-main'
import { AdvancedSearch, SearchService } from '@app/shared/shared-search'
import { ServerConfig } from '@shared/models'
import { HTMLServerConfig } from '@shared/models'
import { RecommendationInfo } from './recommendation-info.model'
import { RecommendationService } from './recommendations.service'
......@@ -15,7 +15,7 @@ import { RecommendationService } from './recommendations.service'
export class RecentVideosRecommendationService implements RecommendationService {
readonly pageSize = 5
private config: ServerConfig
private config: HTMLServerConfig
constructor (
private videos: VideoService,
......@@ -23,13 +23,11 @@ export class RecentVideosRecommendationService implements RecommendationService
private userService: UserService,
private serverService: ServerService
) {
this.config = this.serverService.getTmpConfig()
this.serverService.getConfig()
.subscribe(config => this.config = config)
this.config = this.serverService.getHTMLConfig()
}
getRecommendations (recommendation: RecommendationInfo): Observable<Video[]> {
return this.fetchPage(1, recommendation)
.pipe(
map(videos => {
......
......@@ -28,7 +28,15 @@ import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/sha
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { PeerTubeProblemDocument, ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models'
import {
HTMLServerConfig,
PeerTubeProblemDocument,
ServerErrorCode,
UserVideoRateType,
VideoCaption,
VideoPrivacy,
VideoState
} from '@shared/models'
import {
cleanupVideoWatch,
getStoredP2PEnabled,
......@@ -116,7 +124,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private configSub: Subscription
private liveVideosSub: Subscription
private serverConfig: ServerConfig
private serverConfig: HTMLServerConfig
constructor (
private elementRef: ElementRef,
......@@ -163,21 +171,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
PeertubePlayerManager.initState()
this.serverConfig = this.serverService.getTmpConfig()
this.configSub = this.serverService.getConfig()
.subscribe(config => {
this.serverConfig = config
this.serverConfig = this.serverService.getHTMLConfig()
if (
isWebRTCDisabled() ||
this.serverConfig.tracker.enabled === false ||
getStoredP2PEnabled() === false ||
peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
) {
this.hasAlreadyAcceptedPrivacyConcern = true
}
if (
isWebRTCDisabled() ||
this.serverConfig.tracker.enabled === false ||
getStoredP2PEnabled() === false ||
peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
) {
this.hasAlreadyAcceptedPrivacyConcern = true
}
})
this.paramsSub = this.route.params.subscribe(routeParams => {
const videoId = routeParams[ 'videoId' ]
......
import { Subscription } from 'rxjs'
import { Component, HostBinding, Inject, OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { VideoListHeaderComponent } from '@app/shared/shared-video-miniature'
import { GlobalIconName } from '@app/shared/shared-icons'
import { ServerService } from '@app/core/server/server.service'
import { Subscription } from 'rxjs'
import { AuthService, RedirectService } from '@app/core'
import { ServerService } from '@app/core/server/server.service'
import { GlobalIconName } from '@app/shared/shared-icons'
import { VideoListHeaderComponent } from '@app/shared/shared-video-miniature'
interface VideoTrendingHeaderItem {
label: string
......@@ -67,21 +67,19 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple
}
ngOnInit () {
this.serverService.getConfig()
.subscribe(config => {
const algEnabled = config.trending.videos.algorithms.enabled
const serverConfig = this.serverService.getHTMLConfig()
const algEnabled = serverConfig.trending.videos.algorithms.enabled
this.buttons = this.buttons.map(b => {
b.hidden = !algEnabled.includes(b.value)
this.buttons = this.buttons.map(b => {
b.hidden = !algEnabled.includes(b.value)
// Best is adapted by the user history so
if (b.value === 'best' && !this.auth.isLoggedIn()) {
b.hidden = true
}
// Best is adapted by the user history so
if (b.value === 'best' && !this.auth.isLoggedIn()) {
b.hidden = true
}
return b
})
})
return b
})
this.algorithmChangeSub = this.route.queryParams.subscribe(
queryParams => {
......
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { concat } from 'rxjs'
import { filter, first, map, pairwise } from 'rxjs/operators'
import { filter, first, map, pairwise, switchMap } from 'rxjs/operators'
import { DOCUMENT, PlatformLocation, ViewportScroller } from '@angular/common'
import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
......@@ -15,7 +15,7 @@ import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { LoadingBarService } from '@ngx-loading-bar/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { getShortLocale, is18nPath } from '@shared/core-utils/i18n'
import { BroadcastMessageLevel, ServerConfig, UserRole } from '@shared/models'
import { BroadcastMessageLevel, HTMLServerConfig, ServerConfig, UserRole } from '@shared/models'
import { MenuService } from './core/menu/menu.service'
import { POP_STATE_MODAL_DISMISS } from './helpers'
import { InstanceService } from './shared/shared-instance'
......@@ -35,7 +35,7 @@ export class AppComponent implements OnInit, AfterViewInit {
customCSS: SafeHtml
broadcastMessage: { message: string, dismissable: boolean, class: string } | null = null
private serverConfig: ServerConfig
private serverConfig: HTMLServerConfig
constructor (
@Inject(DOCUMENT) private document: Document,
......@@ -73,9 +73,7 @@ export class AppComponent implements OnInit, AfterViewInit {
ngOnInit () {
document.getElementById('incompatible-browser').className += ' browser-ok'
this.serverConfig = this.serverService.getTmpConfig()
this.serverService.getConfig()
.subscribe(config => this.serverConfig = config)
this.serverConfig = this.serverService.getHTMLConfig()
this.loadPlugins()
this.themeService.initialize()
......@@ -88,10 +86,21 @@ export class AppComponent implements OnInit, AfterViewInit {
}
this.initRouteEvents()
this.injectJS()
this.injectCSS()
this.injectBroadcastMessage()
this.serverService.configReloaded
.subscribe(config => {
this.serverConfig = config
this.injectBroadcastMessage()
this.injectCSS()
// Don't reinject JS since it could conflict with existing one
})
this.initHotkeys()
this.location.onPopState(() => this.modalService.dismissAll(POP_STATE_MODAL_DISMISS))
......@@ -208,69 +217,55 @@ export class AppComponent implements OnInit, AfterViewInit {
).subscribe(() => this.loadingBar.useRef().complete())
}
private injectBroadcastMessage () {
concat(
this.serverService.getConfig().pipe(first()),
this.serverService.configReloaded
).subscribe(async config => {
this.broadcastMessage = null
this.screenService.isBroadcastMessageDisplayed = false
const messageConfig = config.broadcastMessage
private async injectBroadcastMessage () {
this.broadcastMessage = null
this.screenService.isBroadcastMessageDisplayed = false
if (messageConfig.enabled) {
// Already dismissed this message?
if (messageConfig.dismissable && localStorage.getItem(AppComponent.BROADCAST_MESSAGE_KEY) === messageConfig.message) {
return
}
const messageConfig = this.serverConfig.broadcastMessage
const classes: { [id in BroadcastMessageLevel]: string } = {
info: 'alert-info',
warning: 'alert-warning',
error: 'alert-danger'
}
if (messageConfig.enabled) {
// Already dismissed this message?
if (messageConfig.dismissable && localStorage.getItem(AppComponent.BROADCAST_MESSAGE_KEY) === messageConfig.message) {
return
}
this.broadcastMessage = {
message: await this.markdownService.unsafeMarkdownToHTML(messageConfig.message, true),
dismissable: messageConfig.dismissable,
class: classes[messageConfig.level]
}
const classes: { [id in BroadcastMessageLevel]: string } = {
info: 'alert-info',
warning: 'alert-warning',
error: 'alert-danger'
}
this.screenService.isBroadcastMessageDisplayed = true
this.broadcastMessage = {
message: await this.markdownService.unsafeMarkdownToHTML(messageConfig.message, true),
dismissable: messageConfig.dismissable,
class: classes[messageConfig.level]
}
})
this.screenService.isBroadcastMessageDisplayed = true
}
}
private injectJS () {
// Inject JS
this.serverService.getConfig()
.subscribe(config => {
if (config.instance.customizations.javascript) {
try {
// tslint:disable:no-eval
eval(config.instance.customizations.javascript)
} catch (err) {
console.error('Cannot eval custom JavaScript.', err)
}
}
})
if (this.serverConfig.instance.customizations.javascript) {
try {
// tslint:disable:no-eval
eval(this.serverConfig.instance.customizations.javascript)
} catch (err) {
console.error('Cannot eval custom JavaScript.', err)
}
}
}
private injectCSS () {
// Inject CSS if modified (admin config settings)
concat(
this.serverService.getConfig().pipe(first()),
this.serverService.configReloaded
).subscribe(config => {
const headStyle = document.querySelector('style.custom-css-style')
if (headStyle) headStyle.parentNode.removeChild(headStyle)
// We test customCSS if the admin removed the css
if (this.customCSS || config.instance.customizations.css) {
const styleTag = '<style>' + config.instance.customizations.css + '</style>'
this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag)
}
})
const headStyle = document.querySelector('style.custom-css-style')
if (headStyle) headStyle.parentNode.removeChild(headStyle)
// We test customCSS if the admin removed the css
if (this.customCSS || this.serverConfig.instance.customizations.css) {
const styleTag = '<style>' + this.serverConfig.instance.customizations.css + '</style>'
this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag)
}
}
private async loadPlugins () {
......@@ -283,14 +278,18 @@ export class AppComponent implements OnInit, AfterViewInit {
this.authService.userInformationLoaded
.pipe(
map(() => this.authService.getUser()),
filter(user => user.role === UserRole.ADMINISTRATOR)
).subscribe(user => setTimeout(() => this._openAdminModalsIfNeeded(user))) // setTimeout because of ngIf in template
filter(user => user.role === UserRole.ADMINISTRATOR),
switchMap(user => {
return this.serverService.getConfig()
.pipe(map(serverConfig => ({ serverConfig, user })))
})
).subscribe(({ serverConfig, user }) => this._openAdminModalsIfNeeded(serverConfig, user))
}
private async _openAdminModalsIfNeeded (user: User) {
private async _openAdminModalsIfNeeded (serverConfig: ServerConfig, user: User) {
if (user.noWelcomeModal !== true) return this.welcomeModal.show()
if (user.noInstanceConfigWarningModal === true || !this.serverConfig.signup.allowed) return
if (user.noInstanceConfigWarningModal === true || !serverConfig.signup.allowed) return
this.instanceService.getAbout()
.subscribe(about => {
......
......@@ -27,7 +27,7 @@ import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings
registerLocaleData(localeOc, 'oc')
export function loadConfigFactory (server: ServerService) {
return () => server.loadConfig()
return () => server.loadHTMLConfig()
}
@NgModule({
......
......@@ -3,7 +3,7 @@ import { debounceTime } from 'rxjs/operators'
import { Injectable } from '@angular/core'
import { GlobalIconName } from '@app/shared/shared-icons'
import { sortObjectComparator } from '@shared/core-utils/miscs/miscs'
import { ServerConfig } from '@shared/models/server'
import { HTMLServerConfig } from '@shared/models/server'
import { ScreenService } from '../wrappers'
export type MenuLink = {
......@@ -59,7 +59,7 @@ export class MenuService {
this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
}
buildCommonLinks (config: ServerConfig) {
buildCommonLinks (config: HTMLServerConfig) {
let entries: MenuLink[] = [
{
icon: 'globe' as 'globe',
......
......@@ -16,9 +16,7 @@ export class MetaService {
private meta: Meta,
private server: ServerService
) {
this.config = this.server.getTmpConfig()
this.server.getConfig()
.subscribe(config => this.config = config)
this.config = this.server.getHTMLConfig()
}
setTitle (subTitle?: string) {
......
......@@ -20,24 +20,14 @@ export class RedirectService {
private serverService: ServerService
) {
// The config is first loaded from the cache so try to get the default route
const tmpConfig = this.serverService.getTmpConfig()
if (tmpConfig?.instance?.defaultClientRoute) {
this.defaultRoute = tmpConfig.instance.defaultClientRoute
const config = this.serverService.getHTMLConfig()
if (config?.instance?.defaultClientRoute) {
this.defaultRoute = config.instance.defaultClientRoute
}
if (tmpConfig?.trending?.videos?.algorithms?.default) {
this.defaultTrendingAlgorithm = tmpConfig.trending.videos.algorithms.default
if (config?.trending?.videos?.algorithms?.default) {
this.defaultTrendingAlgorithm = config.trending.videos.algorithms.default
}
// Load default route
this.serverService.getConfig()
.subscribe(config => {
const defaultRouteConfig = config.instance.defaultClientRoute
const defaultTrendingConfig = config.trending.videos.algorithms.default
if (defaultRouteConfig) this.defaultRoute = defaultRouteConfig
if (defaultTrendingConfig) this.defaultTrendingAlgorithm = defaultTrendingConfig
})
// Track previous url
this.currentUrl = this.router.url
router.events.subscribe(event => {
......@@ -52,6 +42,7 @@ export class RedirectService {
return this.defaultRoute
}
getDefaultTrendingAlgorithm () {
return this.defaultTrendingAlgorithm
}
......
......@@ -37,9 +37,9 @@ export class ServerService {
) {
}
loadConfig () {
loadHTMLConfig () {
try {
return this.loadConfigLocally()
return this.loadHTMLConfigLocally()
} catch (err) {
// Expected in dev mode since we can't inject the config in the HTML
if (environment.production !== false) {
......@@ -76,6 +76,7 @@ export class ServerService {
.pipe(
tap(config => {
this.config = config
this.htmlConfig = config
this.configLoaded = true
}),
tap(config => {
......@@ -91,8 +92,8 @@ export class ServerService {
return this.configObservable
}
getTmpConfig () {
return this.config
getHTMLConfig () {
return this.htmlConfig
}
getVideoCategories () {
......@@ -156,20 +157,6 @@ export class ServerService {
return this.http.get<ServerStats>(ServerService.BASE_STATS_URL)
}
getDefaultSearchTarget (): Promise<SearchTargetType> {
return this.getConfig().pipe(
map(config => {
const searchIndexConfig = config.search.searchIndex
if (searchIndexConfig.enabled && (searchIndexConfig.isDefaultSearch || searchIndexConfig.disableLocalSearch)) {
return 'search-index'
}
return 'local'
})
).toPromise()
}
private loadAttributeEnum <T extends string | number> (
baseUrl: string,
attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
......@@ -204,12 +191,12 @@ export class ServerService {
)
}
private loadConfigLocally () {
private loadHTMLConfigLocally () {
const configString = window['PeerTubeServerConfig']
if (!configString) {
throw new Error('Could not find PeerTubeServerConfig in HTML')
}
this.config = JSON.parse(configString)
this.htmlConfig = JSON.parse(configString)
}
}
import { first } from 'rxjs/operators'
import { Injectable } from '@angular/core'
import { UserLocalStorageKeys } from '@root-helpers/users'
import { ServerConfig, ServerConfigTheme } from '@shared/models'
import { HTMLServerConfig, ServerConfigTheme } from '@shared/models'
import { environment } from '../../../environments/environment'
import { AuthService } from '../auth'
import { PluginService } from '../plugins/plugin.service'
......@@ -18,7 +18,7 @@ export class ThemeService {
private themeFromLocalStorage: ServerConfigTheme
private themeDOMLinksFromLocalStorage: HTMLLinkElement[] = []
private serverConfig: ServerConfig
private serverConfig: HTMLServerConfig
constructor (
private auth: AuthService,
......@@ -32,18 +32,13 @@ export class ThemeService {
// Try to load from local storage first, so we don't have to wait network requests
this.loadAndSetFromLocalStorage()
this.serverConfig = this.server.getTmpConfig()