Details
I'm working on an extension which integrates draw.io into Phorge.
So far, I'm able to create diagrams and reload them:
However, when I click on the save button I call the PhabricatorFile::newFromFileData method.
This will always create a new file object with a new ID.
If I have for example a reference to this diagram in a wiki page (e.g. {F70}), and I modify the diagram, the wiki page will still show the old version.
Is it possible to update the content of a file object without changing it's ID?
I found some LiskDAO references to IDS_AUTOINCREMENT and IDS_MANUAL, which seem to determine how the ID should be stored.
But I don't quite understand it how to use these.
Event Timeline
By the way it could be a good thing to save versions as "different files".
Honestly, that is what Differential does when you upload new versions, if I understand correctly.
Answers
Is it possible to update the content of a file object without changing it's ID?
No. File contents are immutable.
A good path for this would be to create a Diagram object type, that has a field for a file. It's possible to create a "monogram" for the diagram, so that a reference like {DIAG32} will work in all Remarkup context, but I don't remember the details right now.
Anyway, the Diagrams app looks cool!
Event Timeline
Yes, this was indeed the answer I was afraid of...
I have been looking a bit further and I have new subject related questions:
So, now I need to create a new database table.
How should I create this table? Can this be done by means of the bin/storage upgrade command ?
I tried to install the SQL file with CREATE TABLE statement in it as phorge/src/extensions/drawio/resources/sql/autopatches/20230625.DiagramsCreateTable.sql, but bin/storage upgrade seems to ignore it.
Or does bin/storage upgrade not work for extensions and do I need to execute the CREATE TABLE statement manually ?
To create a DB, you need to add a class like this one to your app:
<?php final class SemiStructuredPatchList extends PhabricatorSQLPatchList { public function getNamespace() { return 'semistructured'; } public function getPatches() { $root = dirname(phutil_get_library_root('semi-structured')); $db_patches = array( // First one is special. 'db.semistructured' => array( 'name' => 'semistructured', 'type' => 'db', 'after' => array(), ), ); $auto = $this->buildPatchesFromDirectory($root.'/resources/sql/'); return $db_patches + $auto; } }
The array in $db_patches contains the "patch" to create the database, and the buildPatchesFromDirectory() call searches the specified directory for SQL scripts (like https://we.phorge.it/source/phorge/browse/master/resources/sql/autopatches/).
Then loading the extension in Phorge, and running bin/storage upgrade will find these patches and apply them.
I got a bit further and got a new issue :-)
I have created the following SQL patch:
CREATE TABLE IF NOT EXISTS {$NAMESPACE}_diagram.diagram ( id int(10) unsigned NOT NULL AUTO_INCREMENT, phid varbinary(64) NOT NULL, version int(10) unsigned NOT NULL, authorPHID varbinary(64) NOT NULL, dateCreated int(10) unsigned NOT NULL, dateModified int(10) unsigned NOT NULL, byteSize bigint(20) unsigned NOT NULL, data longblob NOT NULL, viewPolicy varbinary(64) NOT NULL, editPolicy varbinary(64) NOT NULL, PRIMARY KEY (id), UNIQUE KEY key_version (phid,version), KEY key_authorPHID (authorPHID) );
I have created the following PHP class with the corresponding getConfiguration method:
final class PhabricatorDiagram extends PhabricatorLiskDAO implements PhabricatorPolicyInterface, PhabricatorSubscribableInterface, PhabricatorFlaggableInterface, PhabricatorTokenReceiverInterface, PhabricatorProjectInterface, PhabricatorApplicationTransactionInterface, PhabricatorDestructibleInterface { // ... protected function getConfiguration() { return array( self::CONFIG_COLUMN_SCHEMA => array( // 'phid' => 'phid', 'version' => 'uint32', 'byteSize' => 'uint64', 'data' => 'bytes' ), self::CONFIG_KEY_SCHEMA => array( 'key_version' => array( 'columns' => array('phid', 'version'), 'unique' => true, ), 'key_authorPHID' => array( 'columns' => array('authorPHID'), ), ), ) + parent::getConfiguration(); } // ... }
When I execute bin/storage upgrade, I get the following output:
Before running storage upgrades, you should take down the web interface and stop any running daemons (you can disable this warning with --force). Are you ready to continue? [y/N] y Applying patch "diagram:db.diagram" to host "localhost"... Applying patch "diagram:20230626.DiagramsCreateTable.sql" to host "localhost"... Storage is up to date. Use "storage status" for details. Synchronizing static tables... Verifying database schemata on "localhost"... Found no adjustments for schemata. Target Error phabricator_diagram.diagram.phid Surplus SURPLUS SCHEMATA You have surplus schemata (extra tables or columns which this software does not expect). For information on resolving these issues, see the "Surplus Schemata" section in the "Managing Storage Adjustments" article in the documentation. ANALYZE Analyzing tables... Done. ANALYZED Analyzed 528 table(s).
I'm a bit confused about the "phid surplus" message.
Is this an error or is it something that can be ignored?
It doesn't go away either when I uncomment the phid line in the function above.
And apparently the table should contain a column named "phid" or otherwise it produces another error.
And also to be honest, I made several attempts and each time I executed the following SQL directly in MariaDB afterwards so I could start over from scratch:
delete from phabricator_meta_data.patch_status where patch like 'diagram:%'; drop database phabricator_diagram;
Starting from the end:
The patch script to clear the changes looks correct to me - I do something similar.
There's a GUI for seeing the schema thing - it's very slow because it scans a lot of the database, but it's in /config/cluster/databases/.
As to the PHID column: I use this code, which has the PHID in a special handling.
protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, // <------- self::CONFIG_SERIALIZATION => array( 'customFieldsConfig' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', 'status' => 'text32', 'description' => 'text', ), ) + parent::getConfiguration(); }
I'm not 100% sure what it's doing - I usually just look for a simple object in the codebase and copy from it.
Event Timeline
Cool extension! We still need to build a proper extension ecosystem for Phorge. Maybe we could start with a wiki page linking to known extensions, then you'll have a place to share your extension, should you decide to do so.
Oh yeah - you can even host it here under Phactory: Community Projects (if you want to and it's open-source).
I'm ready to publish a first version, but I don't understand how to publish it under Phactory?
Do I need to publish on Github first ?
We don't really have a concrete flow setup.
I'll file a task and have you fill some details...