Skip to content

Commit

Permalink
Merge pull request #4352 from serlo/staging
Browse files Browse the repository at this point in the history
Deployment
  • Loading branch information
elbotho authored Dec 23, 2024
2 parents 0e324d8 + 0fd968e commit d2267ab
Show file tree
Hide file tree
Showing 86 changed files with 1,669 additions and 194 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"graphql-request": "^7.1.2",
"iframe-resizer": "^4.4.5",
"io-ts": "^2.2.21",
"isomorphic-dompurify": "^2.19.0",
"js-cookie": "^3.0.5",
"json-diff": "^1.0.6",
"katex": "^0.16.11",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/pages/user/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const Profile: NextPage<ProfileProps> = ({ userData }) => {
<ProfileBadges userData={userData} date={date} />
</div>
<div className="serlo-p mt-5 w-full text-1.5xl [grid-area:motivation] sm:mt-0">
{motivation && <>&quot;{motivation}&quot;</>}
{motivation && <>&bdquo;{motivation}&ldquo;</>}
{isOwnProfile &&
!isNewlyRegisteredUser &&
renderEditMotivationLink()}
Expand Down
52 changes: 52 additions & 0 deletions apps/web/src/components/user-tools/share/share-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
faCopy,
faDownload,
faEnvelope,
faFileText,
} from '@fortawesome/free-solid-svg-icons'
import { QRCodeSVG } from 'qrcode.react'
import { MouseEvent, useState, useEffect } from 'react'
Expand All @@ -24,6 +25,7 @@ import { showToastNotice } from '@/helper/show-toast-notice'
export interface ShareModalProps {
isOpen: boolean
setIsOpen: (open: boolean) => void
showCopyContent?: boolean
showPdf?: boolean
path?: string
}
Expand All @@ -39,6 +41,7 @@ interface EntryData {
export function ShareModal({
isOpen,
setIsOpen,
showCopyContent,
showPdf,
path,
}: ShareModalProps) {
Expand Down Expand Up @@ -70,10 +73,41 @@ export function ShareModal({
}
}

async function copyContentToClipboard() {
if (!pathOrId) return
try {
const url = `/api/frontend/bildungsraum-share?href=${encodeURIComponent(pathOrId)}`
const res = await fetch(url)
const data = (await res.json()) as string
if (!res.ok) {
throw new Error(
'injection-content API call failed with error: ' + data.toString()
)
}
await navigator.clipboard.writeText(JSON.stringify(data))
showToastNotice('👌 Erfolgreich kopiert', 'success')
} catch (e) {
// eslint-disable-next-line no-console
console.error(e)
showToastNotice(
'❌ Leider gab es ein Problem beim kopieren. Tut uns leid.',
'warning'
)
}
}

const shareUrl = `${window.location.protocol}//${window.location.host}/${pathOrId}`
const urlEncoded = encodeURIComponent(shareUrl)
const titleEncoded = encodeURIComponent(document.title)

const contentCopy = [
{
title: 'Inhalt kopieren',
icon: faFileText,
onClick: () => copyContentToClipboard(),
},
]

const socialShare = [
{
title: 'E-Mail',
Expand Down Expand Up @@ -156,6 +190,24 @@ export function ShareModal({
{renderButtons(pdfData)}
</>
)}

{showCopyContent ? ( // "de" only
<>
<hr className="mx-side my-4" />
<h3 className="serlo-h3 my-4">Inhalt zum Bearbeiten kopieren</h3>
<p className="serlo-p mb-0 text-base">
Du kannst diesen Inhalt in jedem Serlo Editor weiterbearbeiten: Hier
auf <b>serlo.org</b> und in LMS wie Moodle, Edu-sharing oder
itslearning, die den Serlo Editor eingebaut haben.
<br />
<br />
Dazu einfach auf unten auf &bdquo;Inhalt kopieren&ldquo; klicken,
einen Moment warten und dann Inhalt im Editor Textfeld Deines LMS
einfügen.
</p>
{renderButtons(contentCopy)}
</>
) : null}
</ModalWithCloseButton>
)

Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/components/user-tools/share/share.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ShareModal = dynamic<ShareModalProps>(() =>
)

export function Share({ data, aboveContent }: MoreAuthorToolsProps) {
const { strings } = useInstanceData()
const { lang, strings } = useInstanceData()
const [shareOpen, setShareOpen] = useState(false)

const showPdf =
Expand All @@ -28,6 +28,13 @@ export function Share({ data, aboveContent }: MoreAuthorToolsProps) {
UuidType.Exercise,
].includes(data.typename as UuidType)

const showCopyContent =
lang === 'de' &&
data &&
[UuidType.Article, UuidType.ExerciseGroup, UuidType.Exercise].includes(
data.typename as UuidType
)

return (
<>
<UserToolsItem
Expand All @@ -40,6 +47,7 @@ export function Share({ data, aboveContent }: MoreAuthorToolsProps) {
<ShareModal
isOpen={shareOpen}
setIsOpen={setShareOpen}
showCopyContent={showCopyContent}
showPdf={showPdf}
/>
) : null}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/user/event.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export function Event({
),
comment: (
<p className="font-normal">
&quot;{event.thread.thread.nodes[0].content}&quot;
&bdquo;{event.thread.thread.nodes[0].content}&ldquo;
</p>
),
})
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/data/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ export const instanceData = {
'The provided authentication code is invalid, please try again.',
code4000010:
'Have you already verified your email address?.%break% %verificationLinkText%',
code4000032: "You inserted less than 8 characters.",
code4000032: 'You inserted less than 8 characters.',
code4060004:
'The recovery link is not valid or has already been used. Please try requesting an email again',
code4070001:
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/data/es/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ export const instanceData = {
button: "Compartir",
title: "¡Comparte!",
copyLink: "Copiar enlace",
copySuccess: 'Link copied!',
copyFailed: 'Error copying link!',
copySuccess: "¡Enlace copiado!",
copyFailed: Error al copiar enlace!",
close: "Cerrar",
pdf: "Descargar PDF",
pdfNoSolutions: "PDF sin soluciones"
Expand Down Expand Up @@ -366,7 +366,7 @@ export const instanceData = {
code4000007: "Ya existe una cuenta con el mismo correo electrónico o nombre de usuario.",
code4000008: "El código de autentificación proporcionado no es válido, por favor, inténtalo de nuevo.",
code4000010: "¿Has verificado ya tu dirección de correo electrónico?%break%%verificationLinkText%",
code4000032: "You inserted less than 8 characters.",
code4000032: "Has introducido menos de 8 caracteres.",
code4060004: "El enlace de recuperación no es válido o ya ha sido utilizado. Por favor, intenta solicitar un correo electrónico de nuevo",
code4070001: "El enlace de verificación no es válido o ya ha sido utilizado. Por favor, intenta solicitar un correo electrónico de nuevo.",
code4070005: "Lo sentimos, este enlace de verificación ya no es válido. Por favor, intenta solicitar un correo electrónico de nuevo."
Expand Down
7 changes: 7 additions & 0 deletions apps/web/src/fetcher/graphql-types/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2410,6 +2410,13 @@ type GetCommentsThreadsOldComments_VideoRevision_Fragment = { __typename?: 'Vide

export type GetCommentsThreadsOldCommentsFragment = GetCommentsThreadsOldComments_Applet_Fragment | GetCommentsThreadsOldComments_AppletRevision_Fragment | GetCommentsThreadsOldComments_Article_Fragment | GetCommentsThreadsOldComments_ArticleRevision_Fragment | GetCommentsThreadsOldComments_Course_Fragment | GetCommentsThreadsOldComments_CoursePage_Fragment | GetCommentsThreadsOldComments_CoursePageRevision_Fragment | GetCommentsThreadsOldComments_CourseRevision_Fragment | GetCommentsThreadsOldComments_Event_Fragment | GetCommentsThreadsOldComments_EventRevision_Fragment | GetCommentsThreadsOldComments_Exercise_Fragment | GetCommentsThreadsOldComments_ExerciseGroup_Fragment | GetCommentsThreadsOldComments_ExerciseGroupRevision_Fragment | GetCommentsThreadsOldComments_ExerciseRevision_Fragment | GetCommentsThreadsOldComments_Page_Fragment | GetCommentsThreadsOldComments_PageRevision_Fragment | GetCommentsThreadsOldComments_TaxonomyTerm_Fragment | GetCommentsThreadsOldComments_User_Fragment | GetCommentsThreadsOldComments_Video_Fragment | GetCommentsThreadsOldComments_VideoRevision_Fragment;

export type ShareEditorContentQueryVariables = Exact<{
path: Scalars['String']['input'];
}>;


export type ShareEditorContentQuery = { __typename?: 'Query', uuid?: { __typename: 'Applet', currentRevision?: { __typename?: 'AppletRevision', content: string } | null } | { __typename: 'AppletRevision' } | { __typename: 'Article', currentRevision?: { __typename?: 'ArticleRevision', content: string } | null } | { __typename: 'ArticleRevision' } | { __typename: 'Comment' } | { __typename: 'Course', currentRevision?: { __typename?: 'CourseRevision', content: string } | null } | { __typename: 'CoursePage', currentRevision?: { __typename?: 'CoursePageRevision', content: string } | null } | { __typename: 'CoursePageRevision' } | { __typename: 'CourseRevision' } | { __typename: 'Event', currentRevision?: { __typename?: 'EventRevision', content: string } | null } | { __typename: 'EventRevision' } | { __typename: 'Exercise', currentRevision?: { __typename?: 'ExerciseRevision', content: string } | null } | { __typename: 'ExerciseGroup', currentRevision?: { __typename?: 'ExerciseGroupRevision', content: string } | null } | { __typename: 'ExerciseGroupRevision' } | { __typename: 'ExerciseRevision' } | { __typename: 'Page', currentRevision?: { __typename?: 'PageRevision', content: string } | null } | { __typename: 'PageRevision' } | { __typename: 'TaxonomyTerm' } | { __typename: 'User' } | { __typename: 'Video', currentRevision?: { __typename?: 'VideoRevision', content: string } | null } | { __typename: 'VideoRevision' } | null };

export type InjectionOnlyContentQueryVariables = Exact<{
path: Scalars['String']['input'];
}>;
Expand Down
101 changes: 101 additions & 0 deletions apps/web/src/pages/api/frontend/bildungsraum-share.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { parseDocumentString } from '@editor/static-renderer/helper/parse-document-string'
import type {
EditorArticleDocument,
EditorExerciseDocument,
EditorExerciseGroupDocument,
} from '@editor/types/editor-plugins'
import { gql } from 'graphql-request'
import type { NextApiRequest, NextApiResponse } from 'next'

import { endpoint } from '@/api/endpoint'
import { ShareEditorContentQuery } from '@/fetcher/graphql-types/operations'
import { isProduction } from '@/helper/is-production'

/**
* Allows frontend to copy Serlo content to the clipboard.
* The content is unpacked for consistent pasting in the Editor.
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const href = decodeURIComponent(String(req.query.href))

const [base] = href.split('#')
const path = base.startsWith('/') ? base : `/${base}`

if (!path) {
return res.status(401).json('no path provided')
}

try {
void fetch(endpoint, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify({ query, variables: { path } }),
})
.then((res) => res.json())
.then((data: { data: ShareEditorContentQuery }) => {
if (!data.data?.uuid) {
return res.status(404).json('not found')
}

const uuid = data.data.uuid

if (!Object.hasOwn(uuid, 'currentRevision') || !uuid.currentRevision) {
return res.status(404).json('no current revision')
}

if (uuid.__typename === 'Article') {
const articleDocument = parseDocumentString(
uuid.currentRevision.content
) as EditorArticleDocument
const articleContent = articleDocument.state.content
respondWithContent(articleContent)
return
}

if (
uuid.__typename === 'Exercise' ||
uuid.__typename === 'ExerciseGroup'
) {
const exercise = parseDocumentString(uuid.currentRevision.content) as
| EditorExerciseDocument
| EditorExerciseGroupDocument
respondWithContent({ plugin: 'rows', state: [exercise] })
return
}

return res.status(422).json('unsupported entity type')
})
.catch((e) => {
return res.status(500).json(`${String(e)} at ${path}`)
})
} catch (e) {
return res.status(500).json(`${String(e)} at ${path}`)
}

function respondWithContent(content: any) {
const twoDaysInSeconds = 172800
res.setHeader('Cache-Control', `maxage=${twoDaysInSeconds}`)
if (!isProduction) res.setHeader('Access-Control-Allow-Origin', '*')
res.status(200).json(content)
}
}

const query = gql`
query shareEditorContent($path: String!) {
uuid(alias: { path: $path, instance: de }) {
__typename
... on AbstractEntity {
currentRevision {
content
}
}
}
}
`
3 changes: 2 additions & 1 deletion apps/web/src/serlo-editor-integration/create-renderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
EditorDropzoneImageDocument,
EditorInteractiveVideoDocument,
} from '@editor/types/editor-plugins'
import { sanitizeHref } from '@editor/utils/sanitize-href'
import dynamic from 'next/dynamic'
import { ComponentProps } from 'react'

Expand Down Expand Up @@ -262,7 +263,7 @@ export function createRenderers(): InitRenderersArgs {
linkRenderer: ({ href, children }: ComponentProps<LinkRenderer>) => {
return (
<>
<Link href={href}>{children}</Link>
<Link href={sanitizeHref(href)}>{children}</Link>
<ExtraInfoIfRevisionView>{href}</ExtraInfoIfRevisionView>
</>
)
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/serlo-editor-integration/h5p/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function H5pEditor({ state }: H5pProps) {
Registriere dich mit deiner E-Mail-Adresse und melde dich an.
</li>
<li>
Klicke auf &quot;Neuen Inhalt erstellen&quot; und wähle eines
Klicke auf &bdquo;Neuen Inhalt erstellen&ldquo; und wähle eines
der folgenden Inhaltstypen:
<ul className="serlo-ul">
{Object.values(availableH5pExercises).map((exercise) => (
Expand All @@ -140,7 +140,7 @@ function H5pEditor({ state }: H5pProps) {
</li>
<li>
Erstelle deinen Inhalt, speichere ihn und klicke dann auf
&quot;Inhalt bereitstellen&quot;.
&bdquo;Inhalt bereitstellen&ldquo;.
</li>
<li>Füge die Verknüpfung zur Bereitstellung hier ein:</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/000-general.mobile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Scenario('About Serlo @mobile', ({ I }) => {

// Navigating around
I.click('Pädagogisches Konzept')
I.waitForText('Anleitung für die Lernplattform serlo.org', 5)
I.waitForText('Anleitung für die Lernplattform serlo.org', 15)
I.click('Anleitung für die Lernplattform serlo.org')
I.scrollPageToBottom()
I.click('Community')
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/000-general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Scenario('About Serlo', ({ I }) => {

// Navigating around
I.click('Pädagogisches Konzept')
I.waitForText('Anleitung für die Lernplattform serlo.org', 5)
I.waitForText('Anleitung für die Lernplattform serlo.org', 15)
I.click('Anleitung für die Lernplattform serlo.org')
I.scrollPageToBottom()
// close newsletter modal in case it popped up
Expand Down
6 changes: 3 additions & 3 deletions packages/editor-web-component/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@serlo/editor-web-component",
"version": "0.12.0",
"version": "0.12.1",
"homepage": "https://de.serlo.org/editor",
"bugs": {
"url": "https://github.com/serlo/frontend/issues"
Expand Down Expand Up @@ -40,13 +40,13 @@
"yalc:publish": "yarn build && yalc publish --push --sig"
},
"resolutions": {
"@serlo/editor": "0.20.1"
"@serlo/editor": "0.20.2"
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@rollup/plugin-replace": "^6.0.1",
"@serlo/editor": "0.20.1",
"@serlo/editor": "0.20.2",
"@serlo/typescript-config": "workspace:*",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.3.1",
Expand Down
11 changes: 11 additions & 0 deletions packages/editor/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## Changelog for version 0.20.2

- feat(editor): add learner event handler to package. Thank you [@elbotho](https://github.com/elbotho) in https://github.com/serlo/frontend/pull/4350
- quickfix: show exercise task again. Thank you [@elbotho](https://github.com/elbotho) in https://github.com/serlo/frontend/pull/4347
- Deployment. Thank you [@elbotho](https://github.com/elbotho) in https://github.com/serlo/frontend/pull/4346
- refactor(image-plugin): Show better error messages when the image upload fails. Thank you [@CodingDive](https://github.com/CodingDive) in https://github.com/serlo/frontend/pull/4344
- fix(plugin-rows): whole plugin drag handle bug. Thank you [@hejtful](https://github.com/hejtful) in https://github.com/serlo/frontend/pull/4343
- feat(microadaptivity): integrate AI in final feedback. Thank you [@hugotiburtino](https://github.com/hugotiburtino) in https://github.com/serlo/frontend/pull/4338

**Full Changelog**: https://github.com/serlo/frontend/compare/v0.20.1-editor...v0.20.2-editor

## Changelog for version 0.20.1

- feat(editor-web-component): Expose language prop. Thank you [@CodingDive](https://github.com/CodingDive) in https://github.com/serlo/frontend/pull/4340
Expand Down
Loading

0 comments on commit d2267ab

Please sign in to comment.