Template Gallery: migration to 6.1.0

enable menu for editor redisign
move frontend code to modules
This commit is contained in:
yu-i-i
2026-07-07 14:42:55 +02:00
parent 4499088928
commit de346104a1
54 changed files with 128 additions and 77 deletions
+1 -1
View File
@@ -131,7 +131,7 @@ services:
dockerfile: services/real-time/Dockerfile
env_file:
- dev.env
redis:
image: redis:7
ports:
@@ -37,9 +37,12 @@ const TemplatesManager = {
compiler = ProjectOptionsHandler.normalizeCompiler(
compiler || settings.defaultLatexCompiler
)
imageName = ProjectOptionsHandler.normalizeImageName(
imageName || 'wl_texlive:2018.1'
)
try {
imageName = ProjectOptionsHandler.normalizeImageName(imageName)
} catch {
logger.warn( { templateId, imageName }, 'cannot use the image required by the template, using the default image')
imageName = null
}
const zipUrl = `${settings.apis.filestore.url}/template/${templateId}/v/${templateVersionId}/zip`
const zipReq = await fetchStreamWithResponse(zipUrl, {
@@ -49,6 +52,7 @@ const TemplatesManager = {
const projectName = ProjectDetailsHandler.fixProjectName(templateName)
const dumpPath = `${settings.path.dumpFolder}/${crypto.randomUUID()}_templates-manager`
const writeStream = fs.createWriteStream(dumpPath)
try {
const attributes = {
compiler,
+2
View File
@@ -309,6 +309,8 @@ async function initialize(webRouter, privateApiRouter, publicApiRouter) {
TokenAccessRouter.apply(webRouter)
HistoryRouter.apply(webRouter, privateApiRouter)
await Modules.applyRouter(webRouter, privateApiRouter, publicApiRouter)
if (Settings.enableSubscriptions) {
webRouter.get(
'/user/bonus',
@@ -1,18 +0,0 @@
extends ../layout-react
block entrypointVar
- entrypoint = 'pages/template-gallery'
block vars
block vars
- const suppressNavContentLinks = true
- const suppressNavbar = true
- const suppressFooter = true
- bootstrap5PageStatus = 'enabled' // One of 'disabled', 'enabled', and 'queryStringOnly'
- isWebsiteRedesign = false
block append meta
meta(name="ol-templateCategory" data-type="string" content=category)
block content
#template-gallery-root
+12 -2
View File
@@ -1064,8 +1064,18 @@ module.exports = {
importProjectFromGithubModalWrapper: [],
importProjectFromGithubMenu: [],
editorLeftMenuSync: [],
editorLeftMenuManageTemplate: ['@/features/editor-left-menu/components/actions-manage-template'],
menubarExtraComponents: [],
editorLeftMenuManageTemplate: [
Path.resolve(
__dirname,
'../modules/template-gallery/frontend/js/features/template/components/actions-manage-template'
),
],
menubarExtraComponents: [
Path.resolve(
__dirname,
'../modules/template-gallery/frontend/js/features/template/components/menubar-manage-template'
),
],
insertMenuSections: [],
oauth2Server: [],
managedGroupSubscriptionEnrollmentNotification: [],
@@ -31,7 +31,6 @@
padding-top: 0px;
padding-bottom: 0px;
}
.gallery-header-sort-btn {
font-size: var(--font-size-02);
@@ -1,12 +1,16 @@
import Path from 'node:path'
import { fileURLToPath } from 'node:url'
import logger from '@overleaf/logger'
import ErrorController from '../../../../app/src/Features/Errors/ErrorController.mjs'
import Errors from '../../../../app/src/Features/Errors/Errors.js'
import SessionManager from '../../../../app/src/Features/Authentication/SessionManager.js'
import SessionManager from '../../../../app/src/Features/Authentication/SessionManager.mjs'
import TemplateGalleryManager from'./TemplateGalleryManager.mjs'
import { getUserName } from './TemplateGalleryHelper.mjs'
import { TemplateNameConflictError, RecompileRequiredError } from './TemplateErrors.mjs'
import Settings from '@overleaf/settings'
const __dirname = Path.dirname(fileURLToPath(import.meta.url))
async function createTemplateFromProject(req, res, next) {
const t = req.i18n.translate
try {
@@ -108,7 +112,7 @@ async function templatesCategoryPage(req, res, next) {
category = null
title = t('templates_page_title')
}
res.render('template_gallery/template-gallery', {
res.render(Path.resolve(__dirname, '../views/template_gallery/template-gallery'), {
title,
category,
})
@@ -121,7 +125,7 @@ async function templateDetailsPage(req, res, next) {
const t = req.i18n.translate
try {
const template = await TemplateGalleryManager.getTemplate('_id', req.params.template_id)
res.render('template_gallery/template', {
res.render(Path.resolve(__dirname, '../views/template_gallery/template'), {
title: `${t('template')}: ${template.name}`,
template: JSON.stringify(template),
languages: Settings.languages,
@@ -9,9 +9,9 @@ import ProjectZipStreamManager from '../../../../app/src/Features/Downloads/Proj
import DocumentUpdaterHandler from '../../../../app/src/Features/DocumentUpdater/DocumentUpdaterHandler.mjs'
import ClsiManager from '../../../../app/src/Features/Compile/ClsiManager.mjs'
import CompileManager from '../../../../app/src/Features/Compile/CompileManager.mjs'
import UserGetter from '../../../../app/src/Features/User/UserGetter.js'
import UserGetter from '../../../../app/src/Features/User/UserGetter.mjs'
import { fetchStreamWithResponse } from '@overleaf/fetch-utils'
import { Template } from './models/Template.js'
import { Template } from './models/Template.mjs'
import { RecompileRequiredError } from './TemplateErrors.mjs'
import { cleanHtml } from './CleanHtml.mjs'
@@ -3,7 +3,7 @@ import logger from '@overleaf/logger'
import { Readable } from 'stream'
import settings from '@overleaf/settings'
import { OError } from '../../../../app/src/Features/Errors/Errors.js'
import { Template } from './models/Template.js'
import { Template } from './models/Template.mjs'
import {
validateTemplateInput,
renderTemplateHtmlFields,
@@ -2,7 +2,7 @@ import logger from '@overleaf/logger'
import AuthenticationController from '../../../../app/src/Features/Authentication/AuthenticationController.mjs'
import RateLimiterMiddleware from '../../../../app/src/Features/Security/RateLimiterMiddleware.mjs'
import { RateLimiter } from '../../../../app/src/infrastructure/RateLimiter.js'
import { RateLimiter } from '../../../../app/src/infrastructure/RateLimiter.mjs'
import TemplateGalleryController from './TemplateGalleryController.mjs'
const rateLimiterNewTemplate = new RateLimiter('create-template-from-project', {
@@ -1,9 +1,9 @@
const mongoose = require('../../../../../app/src/infrastructure/Mongoose')
import mongoose from '../../../../../app/src/infrastructure/Mongoose.mjs'
const { Schema } = mongoose
const { ObjectId } = Schema
const TemplateSchema = new Schema(
export const TemplateSchema = new Schema(
{
name: { type: String, required: true },
category: { type: String, required: true },
@@ -29,5 +29,4 @@ const TemplateSchema = new Schema(
{ minimize: false }
)
exports.Template = mongoose.model('Template', TemplateSchema)
exports.TemplateSchema = TemplateSchema
export const Template = mongoose.model('Template', TemplateSchema)
@@ -0,0 +1,15 @@
extends ../../../../../app/views/layout-react
block entrypointVar
- entrypoint = 'modules/template-gallery/pages/template-gallery'
block vars
- const suppressFooter = true
- const suppressPugCookieBanner = true
- isWebsiteRedesign = true
block append meta
meta(name="ol-templateCategory" data-type="string" content=category)
block content
#template-gallery-root
@@ -1,14 +1,12 @@
extends ../layout-react
extends ../../../../../app/views/layout-react
block entrypointVar
- entrypoint = 'pages/template'
- entrypoint = 'modules/template-gallery/pages/template'
block vars
- const suppressNavContentLinks = true
- const suppressNavbar = true
- const suppressFooter = true
- bootstrap5PageStatus = 'enabled' // One of 'disabled', 'enabled', and 'queryStringOnly'
- isWebsiteRedesign = false
- isWebsiteRedesign = true
block append meta
meta(name="ol-template" data-type="json" content=template)
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next'
import { MergeAndOverride } from '../../../../../types/utils'
import { MergeAndOverride } from '../../../../../../../types/utils'
import OLForm from '@/shared/components/ol/ol-form'
import OLFormControl from '@/shared/components/ol/ol-form-control'
import MaterialIcon from '@/shared/components/material-icon'
@@ -1,5 +1,5 @@
import { memo } from 'react'
import { cleanHtml } from '../../../../../modules/template-gallery/app/src/CleanHtml.mjs'
import { cleanHtml } from '../../../../../app/src/CleanHtml.mjs'
function TemplateGalleryEntry({ template }) {
return (
@@ -1,7 +1,7 @@
import { TemplateGalleryProvider } from '../context/template-gallery-context'
import { useTranslation } from 'react-i18next'
import useWaitForI18n from '../../../shared/hooks/use-wait-for-i18n'
import withErrorBoundary from '../../../infrastructure/error-boundary'
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
import withErrorBoundary from '@/infrastructure/error-boundary'
import { GenericErrorBoundaryFallback } from '@/shared/components/generic-error-boundary-fallback'
import getMeta from '@/utils/meta'
import DefaultNavbar from '@/shared/components/navbar/default-navbar'
@@ -9,8 +9,8 @@ import {
} from 'react'
import { Template } from '../../../../../types/template'
import { GetTemplatesResponseBody, Sort } from '../types/api'
import getMeta from '../../../utils/meta'
import useAsync from '../../../shared/hooks/use-async'
import getMeta from '@/utils/meta'
import useAsync from '@/shared/hooks/use-async'
import { getTemplates } from '../util/api'
import sortTemplates from '../util/sort-templates'
import { debugConsole } from '@/utils/debugging'
@@ -1,6 +1,6 @@
import { useTemplateGalleryContext } from '../context/template-gallery-context'
import { Sort } from '../types/api'
import { SortingOrder } from '../../../../../types/sorting-order'
import { SortingOrder } from '../../../../../../../types/sorting-order'
const toggleSort = (order: SortingOrder): SortingOrder => {
return order === 'asc' ? 'desc' : 'asc'
@@ -1,4 +1,4 @@
import { SortingOrder } from '../../../../../types/sorting-order'
import { SortingOrder } from '../../../../../../../types/sorting-order'
import { Template } from '../../../../../types/template'
export type Sort = {
@@ -1,5 +1,5 @@
import { GetTemplatesResponseBody, Sort } from '../types/api'
import { getJSON } from '../../../infrastructure/fetch-json'
import { getJSON } from '@/infrastructure/fetch-json'
export function getTemplates(sortBy: Sort, category: string): Promise<GetTemplatesResponseBody> {
const queryParams = new URLSearchParams({
@@ -1,7 +1,7 @@
import { Sort } from '../types/api'
import { Template } from '../../../../../types/template'
import { SortingOrder } from '../../../../../types/sorting-order'
import { Compare } from '../../../../../types/helpers/array/sort'
import { SortingOrder } from '../../../../../../../types/sorting-order'
import { Compare } from '../../../../../../../types/helpers/array/sort'
const order = (order: SortingOrder, templates: Template[]) => {
return order === 'asc' ? [...templates] : templates.reverse()
@@ -1,11 +1,11 @@
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import * as eventTracking from '../../../infrastructure/event-tracking'
import getMeta from '../../../utils/meta'
import * as eventTracking from '@/infrastructure/event-tracking'
import getMeta from '@/utils/meta'
import OLTooltip from '@/shared/components/ol/ol-tooltip'
import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
import EditorManageTemplateModalWrapper from '../../template/components/manage-template-modal/editor-manage-template-modal-wrapper'
import LeftMenuButton from './left-menu-button'
import { useDetachCompileContext } from '@/shared/context/detach-compile-context'
import EditorManageTemplateModalWrapper from './manage-template-modal/editor-manage-template-modal-wrapper'
import LeftMenuButton from '@/features/editor-left-menu/components/left-menu-button'
type TemplateManageResponse = {
template_id: string
@@ -31,7 +31,7 @@ export default function ActionsManageTemplate() {
({ template_id: templateId }: TemplateManageResponse) => {
location.assign(`/template/${templateId}`)
},
[location]
[]
)
return (
@@ -0,0 +1,52 @@
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import getMeta from '@/utils/meta'
import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
import { useCommandProvider } from '@/features/ide-react/hooks/use-command-provider'
import EditorManageTemplateModalWrapper from './manage-template-modal/editor-manage-template-modal-wrapper'
type TemplateManageResponse = {
template_id: string
}
const MenubarManageTemplate = () => {
const { t } = useTranslation()
const { pdfUrl } = useCompileContext()
const [showManageTemplateModal, setShowManageTemplateModal] = useState(false)
const publishAsTemplateEnabled =
getMeta('ol-showTemplatesServerPro') && pdfUrl
useCommandProvider(
() => [
{
type: 'command',
id: 'manage-template',
label: t('publish_as_template'),
disabled: !publishAsTemplateEnabled,
handler: () => {
setShowManageTemplateModal(true)
},
},
],
[t, publishAsTemplateEnabled]
)
const openTemplate = useCallback(
({ template_id: templateId }: TemplateManageResponse) => {
location.assign(`/template/${templateId}`)
},
[]
)
return (
<EditorManageTemplateModalWrapper
show={showManageTemplateModal}
handleHide={() => setShowManageTemplateModal(false)}
openTemplate={openTemplate}
/>
)
}
export default MenubarManageTemplate
@@ -1,6 +1,6 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import getMeta from '../../../../utils/meta'
import getMeta from '@/utils/meta'
import SettingsMenuSelect from './settings-menu-select'
import type { Optgroup } from './settings-menu-select'
@@ -3,8 +3,8 @@ import getMeta from '@/utils/meta'
import OLCol from '@/shared/components/ol/ol-col'
import OLRow from '@/shared/components/ol/ol-row'
import OLTooltip from '@/shared/components/ol/ol-tooltip'
import { formatDate, fromNowDate } from '../../../utils/dates'
import { cleanHtml } from '../../../../../modules/template-gallery/app/src/CleanHtml.mjs'
import { formatDate, fromNowDate } from '@/utils/dates'
import { cleanHtml } from '../../../../../app/src/CleanHtml.mjs'
import { useTemplateContext } from '../context/template-context'
import DeleteTemplateButton from './delete-template-button'
import EditTemplateButton from './edit-template-button'
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'
import useWaitForI18n from '../../../shared/hooks/use-wait-for-i18n'
import withErrorBoundary from '../../../infrastructure/error-boundary'
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
import withErrorBoundary from '@/infrastructure/error-boundary'
import { GenericErrorBoundaryFallback } from '@/shared/components/generic-error-boundary-fallback'
import DefaultNavbar from '@/shared/components/navbar/default-navbar'
import Footer from '@/shared/components/footer/footer'
@@ -1,10 +1,3 @@
import './../utils/meta'
import '../utils/webpack-public-path'
import './../infrastructure/error-reporter'
import '@/i18n'
import '../features/event-tracking'
import '../features/cookie-banner'
import '../features/link-helpers/slow-link'
import ReactDOM from 'react-dom/client'
import TemplateGalleryRoot from '../features/template-gallery/components/template-gallery-root'
@@ -1,10 +1,3 @@
import './../utils/meta'
import '../utils/webpack-public-path'
import './../infrastructure/error-reporter'
import '@/i18n'
import '../features/event-tracking'
import '../features/cookie-banner'
import '../features/link-helpers/slow-link'
import ReactDOM from 'react-dom/client'
import TemplateRoot from '../features/template/components/template-root'