Page MenuHomePhorge

Profile picture destroy workflow: it should not cause 404 errors (it should set the builtin image)
Open, NormalPublic

Description

Steps to reproduce:

  1. Set your profile image

2 Destroy it with ./bin/remove destroy F1

(Note: you can get the file code by visiting your "Manage" page e.g URL http://phorge.localhost/people/manage/1/)

What happens:

You cause 404 errors on the page.

profile-picture-404-error.jpg (461ร—419 px, 26 KB)

What should happen instead:

The user should have the builtin image.

Current workaround:

Edit the profile picture again, and select something (e.g. Psyduck).

Event Timeline

valerio.bozzolan triaged this task as Normal priority.
valerio.bozzolan created this object in space S1 Public.

It seems it's necessary to be able to "get the user from a profile picture". This is not easy, since the user seems not mentioned in any obvious way from the file object, as already stated in T15407.

Example file object. See "profile":true but see empty authorPHID:

              id: 44
            phid: PHID-FILE-anvsulbhvqsxguk65tid
            name: profile
        mimeType: image/png
        byteSize: 59341
   storageEngine: blob
   storageFormat: raw
   storageHandle: 32
     dateCreated: 1714856499
    dateModified: 1714856499
      authorPHID: NULL                                                                โ† โ† โ† โ† โ†
       secretKey: dzvuzmanku2aayk3x36v
     contentHash: 9de9a20b98478c40fa94b52c2aea3e4db5736994bb88766ce3021f0caa4b49d6
        metadata: {"storage":[],"width":400,"height":400,"canCDN":1,"profile":true}
             ttl: NULL
isExplicitUpload: 0
         mailKey: du26qw5527bwxnttww4k
      viewPolicy: users
       isPartial: 0
      builtinKey: NULL
       isDeleted: 0

Note that profile pictures seem to have not a related entry in the database table file_transformedfile. So we start from the known transformedPHID to find the original file, to eventually get the file.authorPHID. So this is a dead end.


But it seems that every profile picture is attached to (at least) the desired user, so can find the desired user by visiting the database table file_attachment and looking for that filePHID.

Logic pitfalls:

  • other users MAY mention and attach the profile picture here and there, so it's not obvious which attachment is the real profile and further inspection is needed

Performance pitfalls:

  • we only want rows with attachmentMode=attach, but there is not a database index over that ๐Ÿ”ถ (and maybe it has no sense to add one for this)
  • we only want rows with objectPHID starting with PHID-USER-. This already has a database index ๐Ÿ’š
    • but there is no business logic in PHP to easily run a `objectPHID LIKE 'PHID-USER-%'. But this can be easily added I think.

The last point is feasible with something like this:

$where[] = qsprintf(
  $conn,
  'objectPHID LIKE %>',
  $this->objectPHIDPrefix);

So, a sub-task seems: improve PhabricatorFileAttachmentQuery to easily get a specific PHID family (PHID prefix). So we can easily get only users, very efficiently from a database perspective.

When you destroy a PhabricatorFile, it seems this happens:

  1. first, the destruction engine deletes the file itself
  2. so, the PhabricatorFile also nukes the attachments PhabricatorFileAttachment
  3. then, the PhabricatorDestructionEngine extensions are called

The point 2 is quite problematic to me. This means we have not any good "pluggable" / "extension-friendly" point where the People application can re-generate the profile pictures, since they cannot just extend the PhabricatorDestructionEngine, because at that point (3) the attachments are already gone.

Possible solutions:

  1. Change this damn line from $attachment->delete() to $engine->destroyObject($attachment)
  2. Write a brand-new "Pre-Destruction Engine" that is supposed to be fired BEFORE the principal object is destroyed.

Given the fact that we have not infinite time, I will probably proceed with the point n. 1 that seems doable.

OK I've explored the proposal n. 1 about expanding PhabricatorFileAttachment to support PhabricatorDestructibleInterface, but I'm just moving the problem there. The attachment is still immediately nuclearized BEFORE the extensions can do things.

So, let's explore a "Pre-Destruction Engine"...

Again, expanding a PhabricatorFileAttachment to support a destruction engine to post-pone its destruction does NOT work as intended (as it's still destroyed very after the file) since the destroyObjectPermanently() is always supposed to be executed BEFORE the extensions.

diff --git a/src/applications/files/storage/PhabricatorFileAttachment.php b/src/applications/files/storage/PhabricatorFileAttachment.php
index 4b8c106618..446220ed40 100644
--- a/src/applications/files/storage/PhabricatorFileAttachment.php
+++ b/src/applications/files/storage/PhabricatorFileAttachment.php
@@ -4,7 +4,8 @@ final class PhabricatorFileAttachment
   extends PhabricatorFileDAO
   implements
     PhabricatorPolicyInterface,
-    PhabricatorExtendedPolicyInterface {
+    PhabricatorExtendedPolicyInterface,
+    PhabricatorDestructibleInterface {
 
   protected $objectPHID;
   protected $filePHID;
@@ -120,4 +121,12 @@ final class PhabricatorFileAttachment
     );
   }
 
+/* -(  PhabricatorDestructibleInterface  )----------------------------------- */
+
+
+  public function destroyObjectPermanently(
+    PhabricatorDestructionEngine $engine) {
+
+    $this->delete();
+  }
 }

I've also explored another desperate solution, that is, destroying the file attachments using a completely dedicated destruction extension, but that may cause race conditions with other extensions, so we still need to introduce a sort of "destruction priority", to first re-generate the profile pictures and then proceed with the real destruction, but that sounds like we just need a "Before Destruction" engine, that potentially does not destroy anything.

So, new sub-task "Before Destruction" engine.