diff --git a/CHANGELOG.md b/CHANGELOG.md index ba27bc3e9..a93d39228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +**This release adds new migrations, be sure to run them before restarting Mobilizon** + +**This release has a repair step, be sure to run the command right after restarting Mobilizon** + +### Special operations + +* **Reattach media files to their entity.** + When media files were uploaded and added in events and posts bodies, they were only attached to the profile that uploaded them, not to the event or post. This task attaches them back to their entity so that the command to clean orphan media files doesn't remove them. + + * Source install + `MIX_ENV=prod mix mobilizon.maintenance.fix_unattached_media_in_body` + * Docker + `docker-compose exec mobilizon mobilizon_ctl maintenance.fix_unattached_media_in_body` + +### Added + +- **Add a command to clean orphan media files**. There's a `--dry-run` option to see what files would have been deleted. + **Make sure all media files have been reattached properly (see above) before running this command.** + In 1.1.0 a scheduled job will be enabled to clear orphan media files automatically after a while. + +### Fixed + +- Fix inline media that weren't being tracked, so that they are not considered orphans media files. + ## 1.0.2 - 2020-11-15 **This release adds new migrations, be sure to run them before restarting Mobilizon** diff --git a/config/config.exs b/config/config.exs index 6145a5c89..f9c50f8bc 100644 --- a/config/config.exs +++ b/config/config.exs @@ -28,6 +28,8 @@ config :mobilizon, :instance, upload_limit: 10_000_000, avatar_upload_limit: 2_000_000, banner_upload_limit: 4_000_000, + remove_orphan_uploads: true, + orphan_upload_grace_period_hours: 48, email_from: "noreply@localhost", email_reply_to: "noreply@localhost" @@ -250,6 +252,8 @@ config :mobilizon, Oban, crontab: [ {"@hourly", Mobilizon.Service.Workers.BuildSiteMap, queue: :background}, {"17 * * * *", Mobilizon.Service.Workers.RefreshGroups, queue: :background} + # To be activated in Mobilizon 1.2 + # {"@hourly", Mobilizon.Service.Workers.CleanOrphanMediaWorker, queue: :background} ] config :mobilizon, :rich_media, diff --git a/js/src/components/Editor.vue b/js/src/components/Editor.vue index b17fd5089..8117e5526 100644 --- a/js/src/components/Editor.vue +++ b/js/src/components/Editor.vue @@ -212,7 +212,7 @@ import { SEARCH_PERSONS } from "../graphql/search"; import { Actor, IActor, IPerson } from "../types/actor"; import Image from "./Editor/Image"; import MaxSize from "./Editor/MaxSize"; -import { UPLOAD_PICTURE } from "../graphql/upload"; +import { UPLOAD_MEDIA } from "../graphql/upload"; import { listenFileUpload } from "../utils/upload"; import { CURRENT_ACTOR_CLIENT } from "../graphql/actor"; import { IComment } from "../types/comment.model"; @@ -395,7 +395,15 @@ export default class EditorComponent extends Vue { new Image(), new MaxSize({ maxSize: this.maxSize }), ], - onUpdate: ({ getHTML }: { getHTML: Function }) => { + onUpdate: ({ + getHTML, + transaction, + getJSON, + }: { + getHTML: Function; + getJSON: Function; + transaction: unknown; + }) => { this.$emit("input", getHTML()); }, }); @@ -526,14 +534,14 @@ export default class EditorComponent extends Vue { const image = await listenFileUpload(); try { const { data } = await this.$apollo.mutate({ - mutation: UPLOAD_PICTURE, + mutation: UPLOAD_MEDIA, variables: { file: image, name: image.name, }, }); - if (data.uploadPicture && data.uploadPicture.url) { - command({ src: data.uploadPicture.url }); + if (data.uploadMedia && data.uploadMedia.url) { + command({ src: data.uploadMedia.url, "data-media-id": data.uploadMedia.id }); } } catch (error) { console.error(error); diff --git a/js/src/components/Editor/Image.ts b/js/src/components/Editor/Image.ts index 83f984a05..dccce902f 100644 --- a/js/src/components/Editor/Image.ts +++ b/js/src/components/Editor/Image.ts @@ -1,6 +1,7 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck import { Node } from "tiptap"; -import { UPLOAD_PICTURE } from "@/graphql/upload"; +import { UPLOAD_MEDIA } from "@/graphql/upload"; import apolloProvider from "@/vue-apollo"; import ApolloClient from "apollo-client"; import { NormalizedCacheObject } from "apollo-cache-inmemory"; @@ -27,16 +28,18 @@ export default class Image extends Node { title: { default: null, }, + "data-media-id": {}, }, group: "inline", draggable: true, parseDOM: [ { - tag: "img[src]", + tag: "img", getAttrs: (dom: any) => ({ src: dom.getAttribute("src"), title: dom.getAttribute("title"), alt: dom.getAttribute("alt"), + "data-media-id": dom.getAttribute("data-media-id"), }), }, ], @@ -92,13 +95,16 @@ export default class Image extends Node { try { images.forEach(async (image) => { const { data } = await client.mutate({ - mutation: UPLOAD_PICTURE, + mutation: UPLOAD_MEDIA, variables: { file: image, name: image.name, }, }); - const node = schema.nodes.image.create({ src: data.uploadPicture.url }); + const node = schema.nodes.image.create({ + src: data.uploadMedia.url, + "data-media-id": data.uploadMedia.id, + }); const transaction = view.state.tr.insert(coordinates.pos, node); view.dispatch(transaction); }); diff --git a/js/src/components/PictureUpload.vue b/js/src/components/PictureUpload.vue index 64843b69d..593304a66 100644 --- a/js/src/components/PictureUpload.vue +++ b/js/src/components/PictureUpload.vue @@ -60,14 +60,14 @@ figure.image {