From 08f37b90ce3d0714c21ff811912c8b04ca80e44a Mon Sep 17 00:00:00 2001 From: DrosoCode Date: Mon, 31 May 2021 13:36:58 +0200 Subject: [PATCH] feat: onlyoffice editor --- cmd/config_init.go | 4 + cmd/config_set.go | 4 + files/file.go | 3 + frontend/src/i18n/ar.json | 2 + frontend/src/i18n/de.json | 4 +- frontend/src/i18n/en.json | 5 +- frontend/src/i18n/es.json | 4 +- frontend/src/i18n/fr.json | 4 +- frontend/src/i18n/is.json | 4 +- frontend/src/i18n/it.json | 4 +- frontend/src/i18n/ja.json | 4 +- frontend/src/i18n/ko.json | 4 +- frontend/src/i18n/nl-be.json | 4 +- frontend/src/i18n/pl.json | 4 +- frontend/src/i18n/pt-br.json | 4 +- frontend/src/i18n/pt.json | 4 +- frontend/src/i18n/ro.json | 4 +- frontend/src/i18n/ru.json | 4 +- frontend/src/i18n/sv-se.json | 4 +- frontend/src/i18n/zh-cn.json | 4 +- frontend/src/i18n/zh-tw.json | 4 +- frontend/src/utils/constants.ts | 2 + frontend/src/views/Files.vue | 4 + frontend/src/views/files/OnlyOfficeEditor.vue | 170 ++++++++++++++++++ frontend/src/views/settings/Global.vue | 25 +++ http/http.go | 2 + http/onlyoffice.go | 64 +++++++ http/settings.go | 3 + http/static.go | 1 + settings/onlyoffice.go | 7 + settings/settings.go | 1 + test.docx | Bin 0 -> 24698 bytes 32 files changed, 344 insertions(+), 17 deletions(-) create mode 100644 frontend/src/views/files/OnlyOfficeEditor.vue create mode 100644 http/onlyoffice.go create mode 100644 settings/onlyoffice.go create mode 100644 test.docx diff --git a/cmd/config_init.go b/cmd/config_init.go index 60a0f37b..a8426e7c 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -42,6 +42,10 @@ override the options.`, Theme: mustGetString(flags, "branding.theme"), Files: mustGetString(flags, "branding.files"), }, + OnlyOffice: settings.OnlyOffice{ + URL: mustGetString(flags, "onlyoffice.url"), + JWTSecret: mustGetString(flags, "onlyoffice.jwtSecret"), + }, } ser := &settings.Server{ diff --git a/cmd/config_set.go b/cmd/config_set.go index 23ff7e1b..11a40040 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -63,6 +63,10 @@ you want to change. Other options will remain unchanged.`, set.Branding.DisableUsedPercentage = mustGetBool(flags, flag.Name) case "branding.files": set.Branding.Files = mustGetString(flags, flag.Name) + case "onlyoffice.url": + set.OnlyOffice.URL = mustGetString(flags, flag.Name) + case "onlyoffice.jwtSecret": + set.OnlyOffice.JWTSecret = mustGetString(flags, flag.Name) } }) diff --git a/files/file.go b/files/file.go index 03b3a6f9..7d3a695b 100644 --- a/files/file.go +++ b/files/file.go @@ -276,6 +276,9 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { i.Content = string(content) } return nil + case strings.HasPrefix(mimetype, "application/vnd.openxmlformats-officedocument"): + i.Type = "officedocument" + return nil default: i.Type = "blob" } diff --git a/frontend/src/i18n/ar.json b/frontend/src/i18n/ar.json index 0236a9f3..ede9b9c3 100644 --- a/frontend/src/i18n/ar.json +++ b/frontend/src/i18n/ar.json @@ -195,6 +195,8 @@ "newPassword": "كلمة المرور الجديدة", "newPasswordConfirm": "تأكيد كلمة المرور", "newUser": "مستخدم جديد", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "كلمة المرور", "passwordUpdated": "تم تغيير كلمة المرور!", "path": "المسار", diff --git a/frontend/src/i18n/de.json b/frontend/src/i18n/de.json index 2609a5f3..0c45a09d 100644 --- a/frontend/src/i18n/de.json +++ b/frontend/src/i18n/de.json @@ -182,6 +182,8 @@ "newPassword": "Ihr neues Passwort.", "newPasswordConfirm": "Bestätigen Sie Ihr neues Passwort", "newUser": "Neuer Benutzer", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Passwort", "passwordUpdated": "Passwort aktualisiert!", "path": "Pfad", @@ -247,4 +249,4 @@ "seconds": "Sekunden", "unit": "Zeiteinheit" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 00d43b9d..9127ee72 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -195,6 +195,9 @@ "newPassword": "Your new password", "newPasswordConfirm": "Confirm your new password", "newUser": "New User", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", + "onlyOfficeJwtSecret": "Only Office JWT Secret (works only with https, leave blank to disable)", "password": "Password", "passwordUpdated": "Password updated!", "path": "Path", @@ -261,4 +264,4 @@ "seconds": "Seconds", "unit": "Time Unit" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/es.json b/frontend/src/i18n/es.json index 06197a79..43d4dc92 100644 --- a/frontend/src/i18n/es.json +++ b/frontend/src/i18n/es.json @@ -182,6 +182,8 @@ "newPassword": "Tu nueva contraseña", "newPasswordConfirm": "Confirma tu contraseña", "newUser": "Nuevo usuario", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Contraseña", "passwordUpdated": "¡Contraseña actualizada!", "path": "Ruta", @@ -247,4 +249,4 @@ "seconds": "Segundos", "unit": "Unidad" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json index 30725641..d03d26b2 100644 --- a/frontend/src/i18n/fr.json +++ b/frontend/src/i18n/fr.json @@ -181,6 +181,8 @@ "newPassword": "Votre nouveau mot de passe", "newPasswordConfirm": "Confirmation du nouveau mot de passe", "newUser": "Nouvel Utilisateur", + "onlyOffice": "Intégration Only Office", + "onlyOfficeUrl": "URL vers OnlyOffice (laisser vide pour désactiver)", "password": "Mot de passe", "passwordUpdated": "Mot de passe mis à jour !", "path": "", @@ -246,4 +248,4 @@ "seconds": "Secondes", "unit": "Unité de temps" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/is.json b/frontend/src/i18n/is.json index d53874c8..603aa7b9 100644 --- a/frontend/src/i18n/is.json +++ b/frontend/src/i18n/is.json @@ -169,6 +169,8 @@ "newPassword": "Nýja lykilorðið þitt", "newPasswordConfirm": "Staðfestu nýja lykilorðið", "newUser": "Nýr notandi", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Lykilorð", "passwordUpdated": "Lykilorð vistað!", "path": "", @@ -232,4 +234,4 @@ "seconds": "Sekúndur", "unit": "Tímastilling" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/it.json b/frontend/src/i18n/it.json index a03c7f42..64451c56 100644 --- a/frontend/src/i18n/it.json +++ b/frontend/src/i18n/it.json @@ -170,6 +170,8 @@ "newPassword": "La tua nuova password", "newPasswordConfirm": "Conferma la password", "newUser": "Nuovo utente", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Password", "passwordUpdated": "Password aggiornata!", "path": "Percorso", @@ -233,4 +235,4 @@ "seconds": "Secondi", "unit": "Unità di tempo" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/ja.json b/frontend/src/i18n/ja.json index f16a9d16..81634557 100644 --- a/frontend/src/i18n/ja.json +++ b/frontend/src/i18n/ja.json @@ -191,6 +191,8 @@ "newPassword": "新しいパスワード", "newPasswordConfirm": "新しいパスワード(再入力)", "newUser": "新規ユーザー作成", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "パスワード", "passwordUpdated": "パスワードを更新しました!", "path": "パス", @@ -256,4 +258,4 @@ "seconds": "秒", "unit": "時間の単位" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/ko.json b/frontend/src/i18n/ko.json index 046a712b..7ae3de8c 100644 --- a/frontend/src/i18n/ko.json +++ b/frontend/src/i18n/ko.json @@ -169,6 +169,8 @@ "newPassword": "새로운 비밀번호", "newPasswordConfirm": "새로운 비밀번호 확인", "newUser": "새로운 사용자", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "비밀번호", "passwordUpdated": "비밀번호 수정 완료!", "path": "", @@ -232,4 +234,4 @@ "seconds": "초", "unit": "Time Unit" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/nl-be.json b/frontend/src/i18n/nl-be.json index 913e48f0..2e94a45a 100644 --- a/frontend/src/i18n/nl-be.json +++ b/frontend/src/i18n/nl-be.json @@ -169,6 +169,8 @@ "newPassword": "Uw nieuw wachtwoord", "newPasswordConfirm": "Bevestig uw nieuw wachtwoord", "newUser": "Nieuwe gebruiker", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Wachtwoord", "passwordUpdated": "Wachtwoord bijgewerkt!", "path": "", @@ -232,4 +234,4 @@ "seconds": "Seconden", "unit": "Tijdseenheid" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/pl.json b/frontend/src/i18n/pl.json index 9fcd4afa..95ad5fc3 100644 --- a/frontend/src/i18n/pl.json +++ b/frontend/src/i18n/pl.json @@ -169,6 +169,8 @@ "newPassword": "Twoje nowe hasło", "newPasswordConfirm": "Potwierdź swoje hasło", "newUser": "Nowy Użytkownik", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Hasło", "passwordUpdated": "Hasło zostało zapisane!", "path": "Ścieżka", @@ -232,4 +234,4 @@ "seconds": "Sekundy", "unit": "Jednostka czasu" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/pt-br.json b/frontend/src/i18n/pt-br.json index 53d855d1..f78c3b2b 100644 --- a/frontend/src/i18n/pt-br.json +++ b/frontend/src/i18n/pt-br.json @@ -182,6 +182,8 @@ "newPassword": "Nova senha", "newPasswordConfirm": "Confirme a nova senha", "newUser": "Novo usuário", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Senha", "passwordUpdated": "Senha atualizada!", "path": "", @@ -247,4 +249,4 @@ "seconds": "Segundos", "unit": "Unidades de Tempo" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/pt.json b/frontend/src/i18n/pt.json index e7cd9efa..47740180 100644 --- a/frontend/src/i18n/pt.json +++ b/frontend/src/i18n/pt.json @@ -170,6 +170,8 @@ "newPassword": "Nova palavra-passe", "newPasswordConfirm": "Confirme a nova palavra-passe", "newUser": "Novo utilizador", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Palavra-passe", "passwordUpdated": "Palavra-passe atualizada!", "path": "", @@ -233,4 +235,4 @@ "seconds": "Segundos", "unit": "Unidades de tempo" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/ro.json b/frontend/src/i18n/ro.json index 6d564461..068c3c4f 100644 --- a/frontend/src/i18n/ro.json +++ b/frontend/src/i18n/ro.json @@ -169,6 +169,8 @@ "newPassword": "Noua ta parolă", "newPasswordConfirm": "Confirmă noua parolă", "newUser": "Utilizator nou", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Parola", "passwordUpdated": "Parola actualizată!", "path": "", @@ -232,4 +234,4 @@ "seconds": "Secunde", "unit": "Unitate de timp" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/ru.json b/frontend/src/i18n/ru.json index bb6e8c92..8d261ae9 100644 --- a/frontend/src/i18n/ru.json +++ b/frontend/src/i18n/ru.json @@ -177,6 +177,8 @@ "newPassword": "Новый пароль", "newPasswordConfirm": "Повтор нового пароля", "newUser": "Новый пользователь", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Пароль", "passwordUpdated": "Пароль обновлен!", "path": "Путь", @@ -242,4 +244,4 @@ "seconds": "Секунды", "unit": "Единица времени" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/sv-se.json b/frontend/src/i18n/sv-se.json index d6bb1676..b53257c6 100644 --- a/frontend/src/i18n/sv-se.json +++ b/frontend/src/i18n/sv-se.json @@ -169,6 +169,8 @@ "newPassword": "Ditt nya lösenord", "newPasswordConfirm": "Bekräfta ditt nya lösenord", "newUser": "Ny användare", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "Lösenord", "passwordUpdated": "Lösenord uppdaterat", "path": "", @@ -232,4 +234,4 @@ "seconds": "Sekunder", "unit": "Tidsenhet" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/zh-cn.json b/frontend/src/i18n/zh-cn.json index 51801a59..a0ab108c 100644 --- a/frontend/src/i18n/zh-cn.json +++ b/frontend/src/i18n/zh-cn.json @@ -191,6 +191,8 @@ "newPassword": "你的新密码", "newPasswordConfirm": "再次输入以确认你的新密码", "newUser": "新建用户", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "密码", "passwordUpdated": "密码已更新!", "path": "路径", @@ -256,4 +258,4 @@ "seconds": "秒", "unit": "时间单位" } -} +} \ No newline at end of file diff --git a/frontend/src/i18n/zh-tw.json b/frontend/src/i18n/zh-tw.json index 26da1978..2f176dae 100644 --- a/frontend/src/i18n/zh-tw.json +++ b/frontend/src/i18n/zh-tw.json @@ -169,6 +169,8 @@ "newPassword": "您的新密碼", "newPasswordConfirm": "重輸一遍新密碼", "newUser": "建立使用者", + "onlyOffice": "Only Office Integration", + "onlyOfficeUrl": "Only Office URL (leave blank to disable)", "password": "密碼", "passwordUpdated": "密碼已更新!", "path": "", @@ -232,4 +234,4 @@ "seconds": "秒", "unit": "時間單位" } -} +} \ No newline at end of file diff --git a/frontend/src/utils/constants.ts b/frontend/src/utils/constants.ts index 3f131b1a..c437b0d2 100644 --- a/frontend/src/utils/constants.ts +++ b/frontend/src/utils/constants.ts @@ -18,6 +18,7 @@ const enableExec: boolean = window.FileBrowser.EnableExec; const tusSettings = window.FileBrowser.TusSettings; const origin = window.location.origin; const tusEndpoint = `/api/tus`; +const onlyOffice = window.FileBrowser.OnlyOffice; export { name, @@ -39,4 +40,5 @@ export { tusSettings, origin, tusEndpoint, + onlyOffice, }; diff --git a/frontend/src/views/Files.vue b/frontend/src/views/Files.vue index 2e82fc6b..ea6196d2 100644 --- a/frontend/src/views/Files.vue +++ b/frontend/src/views/Files.vue @@ -37,6 +37,7 @@ import { storeToRefs } from "pinia"; import { useFileStore } from "@/stores/file"; import { useLayoutStore } from "@/stores/layout"; import { useUploadStore } from "@/stores/upload"; +import { onlyOffice } from "@/utils/constants"; import HeaderBar from "@/components/header/HeaderBar.vue"; import Breadcrumbs from "@/components/Breadcrumbs.vue"; @@ -47,6 +48,7 @@ import FileListing from "@/views/files/FileListing.vue"; import { StatusError } from "@/api/utils"; const Editor = defineAsyncComponent(() => import("@/views/files/Editor.vue")); const Preview = defineAsyncComponent(() => import("@/views/files/Preview.vue")); +const OnlyOfficeEditor = defineAsyncComponent(() => import("@/views/files/OnlyOfficeEditor.vue")); const layoutStore = useLayoutStore(); const fileStore = useFileStore(); @@ -77,6 +79,8 @@ const currentView = computed(() => { fileStore.req.type === "textImmutable" ) { return Editor; + } else if (fileStore.req.type === "officedocument" && onlyOffice !== "") { + return OnlyOfficeEditor; } else { return Preview; } diff --git a/frontend/src/views/files/OnlyOfficeEditor.vue b/frontend/src/views/files/OnlyOfficeEditor.vue new file mode 100644 index 00000000..bb76dce8 --- /dev/null +++ b/frontend/src/views/files/OnlyOfficeEditor.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/frontend/src/views/settings/Global.vue b/frontend/src/views/settings/Global.vue index 40a3ec04..1b77e9e5 100644 --- a/frontend/src/views/settings/Global.vue +++ b/frontend/src/views/settings/Global.vue @@ -136,6 +136,31 @@ />

+ +

{{ $t("settings.onlyOffice") }}

+ +

+ + +

+

+ + +

diff --git a/http/http.go b/http/http.go index 620c43fd..1da832bd 100644 --- a/http/http.go +++ b/http/http.go @@ -60,6 +60,8 @@ func NewHandler( users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET") users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE") + api.PathPrefix("/onlyoffice").Handler(monkey(onlyofficeCallbackHandler, "/api/onlyoffice/callback")).Methods("POST") + api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET") api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE") api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST") diff --git a/http/onlyoffice.go b/http/onlyoffice.go new file mode 100644 index 00000000..55d6416e --- /dev/null +++ b/http/onlyoffice.go @@ -0,0 +1,64 @@ +package http + +import ( + "encoding/json" + "errors" + "io" + "net/http" +) + +type OnlyOfficeCallback struct { + ChangesURL string `json:"changesurl,omitempty"` + Key string `json:"key"` + Status int `json:"status"` + URL string `json:"url,omitempty"` + Users []string `json:"users,omitempty"` + UserData string `json:"userdata,omitempty"` +} + +var onlyofficeCallbackHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { + body, err := io.ReadAll(r.Body) + if err != nil { + return http.StatusInternalServerError, err + } + + var data OnlyOfficeCallback + err = json.Unmarshal(body, &data) + if err != nil { + return http.StatusInternalServerError, err + } + + if data.Status == 2 || data.Status == 6 { + docPath := r.URL.Query().Get("save") + if docPath == "" { + return http.StatusInternalServerError, errors.New("unable to get file save path") + } + + if !d.user.Perm.Modify || !d.Check(docPath) { + return http.StatusForbidden, nil + } + + doc, err := http.Get(data.URL) + if err != nil { + return http.StatusInternalServerError, err + } + defer doc.Body.Close() + + err = d.RunHook(func() error { + _, writeErr := writeFile(d.user.Fs, docPath, doc.Body) + if writeErr != nil { + return writeErr + } + return nil + }, "save", docPath, "", d.user) + + if err != nil { + return http.StatusInternalServerError, err + } + } + + resp := map[string]int{ + "error": 0, + } + return renderJSON(w, r, resp) +}) diff --git a/http/settings.go b/http/settings.go index de3f22ad..4c294148 100644 --- a/http/settings.go +++ b/http/settings.go @@ -18,6 +18,7 @@ type settingsData struct { Tus settings.Tus `json:"tus"` Shell []string `json:"shell"` Commands map[string][]string `json:"commands"` + OnlyOffice settings.OnlyOffice `json:"onlyoffice"` } var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { @@ -31,6 +32,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, Tus: d.settings.Tus, Shell: d.settings.Shell, Commands: d.settings.Commands, + OnlyOffice: d.settings.OnlyOffice, } return renderJSON(w, r, data) @@ -52,6 +54,7 @@ var settingsPutHandler = withAdmin(func(_ http.ResponseWriter, r *http.Request, d.settings.Tus = req.Tus d.settings.Shell = req.Shell d.settings.Commands = req.Commands + d.settings.OnlyOffice = req.OnlyOffice err = d.store.Settings.Save(d.settings) return errToStatus(err), err diff --git a/http/static.go b/http/static.go index 47d828c6..d8f19c4c 100644 --- a/http/static.go +++ b/http/static.go @@ -46,6 +46,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys "ResizePreview": d.server.ResizePreview, "EnableExec": d.server.EnableExec, "TusSettings": d.settings.Tus, + "OnlyOffice": d.settings.OnlyOffice, } if d.settings.Branding.Files != "" { diff --git a/settings/onlyoffice.go b/settings/onlyoffice.go new file mode 100644 index 00000000..1b6cb185 --- /dev/null +++ b/settings/onlyoffice.go @@ -0,0 +1,7 @@ +package settings + +// OnlyOffice contains the onlyoffice server connection settings of the app. +type OnlyOffice struct { + URL string `json:"url"` + JWTSecret string `json:"jwtSecret"` +} diff --git a/settings/settings.go b/settings/settings.go index 22908396..1ee0b1c1 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -27,6 +27,7 @@ type Settings struct { Commands map[string][]string `json:"commands"` Shell []string `json:"shell"` Rules []rules.Rule `json:"rules"` + OnlyOffice OnlyOffice `json:"onlyoffice"` } // GetRules implements rules.Provider. diff --git a/test.docx b/test.docx new file mode 100644 index 0000000000000000000000000000000000000000..adf9a8c004ab9f4965602b5c8cf748e335541a6e GIT binary patch literal 24698 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 literal 0 HcmV?d00001