mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Template Gallery: migration to 6.1.0
enable menu for editor redisign move frontend code to modules
This commit is contained in:
@@ -32,8 +32,18 @@ const TemplatesManager = {
|
||||
templateVersionId,
|
||||
userId,
|
||||
imageName,
|
||||
language
|
||||
spellCheckLanguage
|
||||
) {
|
||||
|
||||
compiler = ProjectOptionsHandler.normalizeCompiler(compiler || 'pdflatex')
|
||||
|
||||
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, {
|
||||
signal: AbortSignal.timeout(TIMEOUT),
|
||||
@@ -42,8 +52,15 @@ const TemplatesManager = {
|
||||
const projectName = ProjectDetailsHandler.fixProjectName(templateName)
|
||||
const dumpPath = `${settings.path.dumpFolder}/${crypto.randomUUID()}_templates-manager`
|
||||
const writeStream = fs.createWriteStream(dumpPath)
|
||||
|
||||
try {
|
||||
const attributes = {}
|
||||
const attributes = {
|
||||
compiler,
|
||||
imageName,
|
||||
spellCheckLanguage,
|
||||
}
|
||||
if (brandVariationId) attributes.brandVariationId = brandVariationId
|
||||
|
||||
await pipeline(zipReq.stream, writeStream)
|
||||
|
||||
if (zipReq.response.status !== 200) {
|
||||
@@ -73,13 +90,22 @@ const TemplatesManager = {
|
||||
return undefined
|
||||
})
|
||||
|
||||
await TemplatesManager._setCompiler(project._id, compiler)
|
||||
await TemplatesManager._setImage(project._id, imageName)
|
||||
await TemplatesManager._setMainFile(project._id, mainFile)
|
||||
await TemplatesManager._setSpellCheckLanguage(project._id, language)
|
||||
await TemplatesManager._setBrandVariationId(project._id, brandVariationId)
|
||||
await TemplatesManager._setMainFile(project, mainFile)
|
||||
|
||||
await prepareClsiCacheInBackground
|
||||
const found = await prepareClsiCacheInBackground
|
||||
if (found === false && project.rootDoc_id) {
|
||||
ClsiCacheManager.createTemplateClsiCache({
|
||||
templateVersionId,
|
||||
project,
|
||||
fileEntries,
|
||||
docEntries,
|
||||
}).catch(err => {
|
||||
logger.error(
|
||||
{ err, templateVersionId },
|
||||
'failed to create template clsi-cache'
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return project
|
||||
} finally {
|
||||
@@ -87,41 +113,15 @@ const TemplatesManager = {
|
||||
}
|
||||
},
|
||||
|
||||
async _setCompiler(projectId, compiler) {
|
||||
if (compiler == null) {
|
||||
return
|
||||
}
|
||||
await ProjectOptionsHandler.setCompiler(projectId, compiler)
|
||||
},
|
||||
|
||||
async _setImage(projectId, imageName) {
|
||||
try {
|
||||
await ProjectOptionsHandler.setImageName(projectId, imageName)
|
||||
} catch {
|
||||
logger.warn({ imageName: imageName }, 'not available')
|
||||
await ProjectOptionsHandler.setImageName(projectId, settings.currentImageName)
|
||||
}
|
||||
},
|
||||
|
||||
async _setMainFile(projectId, mainFile) {
|
||||
async _setMainFile(project, mainFile) {
|
||||
if (mainFile == null) {
|
||||
return
|
||||
}
|
||||
await ProjectRootDocManager.setRootDocFromName(projectId, mainFile)
|
||||
},
|
||||
|
||||
async _setSpellCheckLanguage(projectId, language) {
|
||||
if (language == null) {
|
||||
return
|
||||
}
|
||||
await ProjectOptionsHandler.setSpellCheckLanguage(projectId, language)
|
||||
},
|
||||
|
||||
async _setBrandVariationId(projectId, brandVariationId) {
|
||||
if (brandVariationId == null) {
|
||||
return
|
||||
}
|
||||
await ProjectOptionsHandler.setBrandVariationId(projectId, brandVariationId)
|
||||
const rootDocId = await ProjectRootDocManager.setRootDocFromName(
|
||||
project._id,
|
||||
mainFile
|
||||
)
|
||||
if (rootDocId) project.rootDoc_id = rootDocId
|
||||
},
|
||||
|
||||
async fetchFromV1(templateId) {
|
||||
|
||||
@@ -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
|
||||
@@ -1041,8 +1041,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'
|
||||
),
|
||||
],
|
||||
oauth2Server: [],
|
||||
managedGroupSubscriptionEnrollmentNotification: [],
|
||||
managedGroupEnrollmentInvite: [],
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
|
||||
.gallery-header-sort-btn {
|
||||
font-size: var(--font-size-02);
|
||||
border: 0;
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user